zathura.c 43.5 KB
Newer Older
1
2
/* See LICENSE file for license and copyright information */

3
#define _DEFAULT_SOURCE
Sebastian Ramacher's avatar
Sebastian Ramacher committed
4
#define _XOPEN_SOURCE 700
5

6
#include <errno.h>
Moritz Lipp's avatar
Moritz Lipp committed
7
#include <stdlib.h>
Pavel Borzenkov's avatar
Pavel Borzenkov committed
8
#include <unistd.h>
9
#include <math.h>
10
#include <string.h>
Moritz Lipp's avatar
Moritz Lipp committed
11

12
13
14
15
16
#include <girara/datastructures.h>
#include <girara/utils.h>
#include <girara/session.h>
#include <girara/statusbar.h>
#include <girara/settings.h>
17
#include <girara/shortcuts.h>
Sebastian Ramacher's avatar
Sebastian Ramacher committed
18
#include <girara/template.h>
19
#include <glib/gstdio.h>
20
#include <glib/gi18n.h>
21

Moritz Lipp's avatar
Moritz Lipp committed
22
23
24
25
#ifdef G_OS_UNIX
#include <glib-unix.h>
#endif

26
#include "bookmarks.h"
Moritz Lipp's avatar
Moritz Lipp committed
27
28
#include "callbacks.h"
#include "config.h"
29
30
31
32
#ifdef WITH_SQLITE
#include "database-sqlite.h"
#endif
#include "database-plain.h"
33
#include "document.h"
Moritz Lipp's avatar
Moritz Lipp committed
34
#include "shortcuts.h"
Moritz Lipp's avatar
Moritz Lipp committed
35
#include "zathura.h"
Moritz Lipp's avatar
Moritz Lipp committed
36
#include "utils.h"
Moritz Lipp's avatar
Moritz Lipp committed
37
#include "marks.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
38
#include "render.h"
Moritz Lipp's avatar
Moritz Lipp committed
39
#include "page.h"
40
#include "page-widget.h"
41
#include "plugin.h"
42
#include "adjustment.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
43
#include "dbus-interface.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
44
#include "css-definitions.h"
45
#include "synctex.h"
Moritz Lipp's avatar
Moritz Lipp committed
46

Moritz Lipp's avatar
Moritz Lipp committed
47
typedef struct zathura_document_info_s {
Moritz Lipp's avatar
Update    
Moritz Lipp committed
48
  zathura_t* zathura;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
49
50
  char* path;
  char* password;
51
  int page_number;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
52
53
  char* mode;
  char* synctex;
Moritz Lipp's avatar
Update    
Moritz Lipp committed
54
55
} zathura_document_info_t;

56

57
static gboolean document_info_open(gpointer data);
Moritz Lipp's avatar
Moritz Lipp committed
58

Moritz Lipp's avatar
Moritz Lipp committed
59
60
61
62
#ifdef G_OS_UNIX
static gboolean zathura_signal_sigterm(gpointer data);
#endif

Sebastian Ramacher's avatar
Sebastian Ramacher committed
63
64
65
66
67
68
69
70
71
72
73
74
75
76
static void
free_document_info(zathura_document_info_t* document_info)
{
  if (document_info == NULL) {
    return;
  }

  g_free(document_info->path);
  g_free(document_info->password);
  g_free(document_info->mode);
  g_free(document_info->synctex);
  g_free(document_info);
}

Moritz Lipp's avatar
Moritz Lipp committed
77
/* function implementation */
Moritz Lipp's avatar
Moritz Lipp committed
78
zathura_t*
79
zathura_create(void)
80
{
81
82
83
84
  zathura_t* zathura = g_try_malloc0(sizeof(zathura_t));
  if (zathura == NULL) {
    return NULL;
  }
85

86
  /* global settings */
87
  zathura->global.search_direction = FORWARD;
88

89
  /* plugins */
Moritz Lipp's avatar
Moritz Lipp committed
90
91
  zathura->plugins.manager = zathura_plugin_manager_new();
  if (zathura->plugins.manager == NULL) {
92
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
93
  }
94

95
  /* UI */
96
97
  zathura->ui.session = girara_session_create();
  if (zathura->ui.session == NULL) {
98
    goto error_out;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
99
100
  }

Moritz Lipp's avatar
Moritz Lipp committed
101
102
103
104
105
#ifdef G_OS_UNIX
  /* signal handler */
  zathura->signals.sigterm = g_unix_signal_add(SIGTERM, zathura_signal_sigterm, zathura);
#endif

106
107
108
109
110
111
112
113
114
115
  zathura->ui.session->global.data = zathura;

  return zathura;

error_out:

  zathura_free(zathura);

  return NULL;
}
Sebastian Ramacher's avatar
Sebastian Ramacher committed
116

117
118
119
120
121
bool
zathura_init(zathura_t* zathura)
{
  if (zathura == NULL) {
    return false;
122
123
  }

124
  /* create zathura (config/data) directory */
125
126
127
128
129
130
131
  if (g_mkdir_with_parents(zathura->config.config_dir, 0771) == -1) {
    girara_error("Could not create '%s': %s", zathura->config.config_dir, strerror(errno));
  }

  if (g_mkdir_with_parents(zathura->config.data_dir, 0771) == -1) {
    girara_error("Could not create '%s': %s", zathura->config.data_dir, strerror(errno));
  }
132

133
134
135
  /* load plugins */
  zathura_plugin_manager_load(zathura->plugins.manager);

136
137
  /* configuration */
  config_load_default(zathura);
138
  config_load_files(zathura);
139

140
  /* UI */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
141
  if (girara_session_init(zathura->ui.session, "zathura") == false) {
Moritz Lipp's avatar
Moritz Lipp committed
142
    goto error_free;
143
144
145
  }

  /* girara events */
Moritz Lipp's avatar
Moritz Lipp committed
146
147
  zathura->ui.session->events.buffer_changed  = cb_buffer_changed;
  zathura->ui.session->events.unknown_command = cb_unknown_command;
148

149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
  /* zathura signals */
  zathura->signals.refresh_view = g_signal_new("refresh-view",
                                               GTK_TYPE_WIDGET,
                                               G_SIGNAL_RUN_LAST,
                                               0,
                                               NULL,
                                               NULL,
                                               g_cclosure_marshal_generic,
                                               G_TYPE_NONE,
                                               1,
                                               G_TYPE_POINTER);

  g_signal_connect(G_OBJECT(zathura->ui.session->gtk.view), "refresh-view",
                   G_CALLBACK(cb_refresh_view), zathura);

164
  /* page view */
Moritz Lipp's avatar
Moritz Lipp committed
165
  zathura->ui.page_widget = gtk_grid_new();
166
167
  gtk_grid_set_row_homogeneous(GTK_GRID(zathura->ui.page_widget), TRUE);
  gtk_grid_set_column_homogeneous(GTK_GRID(zathura->ui.page_widget), TRUE);
Moritz Lipp's avatar
Moritz Lipp committed
168
  if (zathura->ui.page_widget == NULL) {
169
170
171
    goto error_free;
  }

172
  g_signal_connect(G_OBJECT(zathura->ui.session->gtk.window), "size-allocate", G_CALLBACK(cb_view_resized), zathura);
173

174
  GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(
175
                 GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
176
177
178

  /* Connect hadjustment signals */
  g_signal_connect(G_OBJECT(hadjustment), "value-changed",
179
      G_CALLBACK(cb_view_hadjustment_value_changed), zathura);
180
181
182
183
  g_signal_connect(G_OBJECT(hadjustment), "changed",
      G_CALLBACK(cb_view_hadjustment_changed), zathura);

  GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(
184
                 GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
185
186
187
188
189
190

  /* Connect vadjustment signals */
  g_signal_connect(G_OBJECT(vadjustment), "value-changed",
      G_CALLBACK(cb_view_vadjustment_value_changed), zathura);
  g_signal_connect(G_OBJECT(vadjustment), "changed",
      G_CALLBACK(cb_view_vadjustment_changed), zathura);
Moritz Lipp's avatar
Moritz Lipp committed
191

192
  /* page view alignment */
193
194
  gtk_widget_set_halign(zathura->ui.page_widget, GTK_ALIGN_CENTER);
  gtk_widget_set_valign(zathura->ui.page_widget, GTK_ALIGN_CENTER);
195

196
197
198
199
  gtk_widget_set_hexpand_set(zathura->ui.page_widget, TRUE);
  gtk_widget_set_hexpand(zathura->ui.page_widget, FALSE);
  gtk_widget_set_vexpand_set(zathura->ui.page_widget, TRUE);
  gtk_widget_set_vexpand(zathura->ui.page_widget, FALSE);
200

201
  gtk_widget_show(zathura->ui.page_widget);
Moritz Lipp's avatar
Moritz Lipp committed
202

203
  /* statusbar */
Moritz Lipp's avatar
Moritz Lipp committed
204
205
  zathura->ui.statusbar.file = girara_statusbar_item_add(zathura->ui.session, TRUE, TRUE, TRUE, NULL);
  if (zathura->ui.statusbar.file == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
206
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
207
208
  }

Moritz Lipp's avatar
Moritz Lipp committed
209
210
  zathura->ui.statusbar.buffer = girara_statusbar_item_add(zathura->ui.session, FALSE, FALSE, FALSE, NULL);
  if (zathura->ui.statusbar.buffer == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
211
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
212
213
  }

Moritz Lipp's avatar
Moritz Lipp committed
214
  zathura->ui.statusbar.page_number = girara_statusbar_item_add(zathura->ui.session, FALSE, FALSE, FALSE, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
215
  if (zathura->ui.statusbar.page_number == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
216
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
217
218
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
219
  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, _("[No name]"));
Moritz Lipp's avatar
Moritz Lipp committed
220

Moritz Lipp's avatar
Moritz Lipp committed
221
  /* signals */
222
  g_signal_connect(G_OBJECT(zathura->ui.session->gtk.window), "destroy", G_CALLBACK(cb_destroy), zathura);
223

224
  /* database */
225
226
227
228
  char* database = NULL;
  girara_setting_get(zathura->ui.session, "database", &database);

  if (g_strcmp0(database, "plain") == 0) {
229
    girara_debug("Using plain database backend.");
230
231
232
    zathura->database = zathura_plaindatabase_new(zathura->config.data_dir);
#ifdef WITH_SQLITE
  } else if (g_strcmp0(database, "sqlite") == 0) {
233
    girara_debug("Using sqlite database backend.");
234
235
236
237
    char* tmp = g_build_filename(zathura->config.data_dir, "bookmarks.sqlite", NULL);
    zathura->database = zathura_sqldatabase_new(tmp);
    g_free(tmp);
#endif
Sebastian Ramacher's avatar
Sebastian Ramacher committed
238
  } else if (g_strcmp0(database, "null") != 0) {
239
240
241
    girara_error("Database backend '%s' is not supported.", database);
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
242
  if (zathura->database == NULL && g_strcmp0(database, "null") != 0) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
243
    girara_error("Unable to initialize database. Bookmarks won't be available.");
244
  } else {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
245
    g_object_set(G_OBJECT(zathura->ui.session->command_history), "io", zathura->database, NULL);
246
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
247
  g_free(database);
248

249
  /* bookmarks */
250
  zathura->bookmarks.bookmarks = girara_sorted_list_new2((girara_compare_function_t) zathura_bookmarks_compare,
Moritz Lipp's avatar
Moritz Lipp committed
251
                                 (girara_free_function_t) zathura_bookmark_free);
252

253
  /* jumplist */
254
255
  int jumplist_size = 20;
  girara_setting_get(zathura->ui.session, "jumplist-size", &jumplist_size);
256

257
258
  zathura->jumplist.max_size = jumplist_size < 0 ? 0 : jumplist_size;
  zathura->jumplist.list = NULL;
259
260
  zathura->jumplist.size = 0;
  zathura->jumplist.cur = NULL;
261

Sebastian Ramacher's avatar
Sebastian Ramacher committed
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
  /* CSS for index mode */
  GiraraTemplate* csstemplate = girara_session_get_template(zathura->ui.session);

  static const char* index_settings[] = {
    "index-fg",
    "index-bg",
    "index-active-fg",
    "index-active-bg"
  };

  for (size_t s = 0; s < LENGTH(index_settings); ++s) {
    girara_template_add_variable(csstemplate, index_settings[s]);

    char* tmp_value = NULL;
    GdkRGBA rgba = { 0, 0, 0, 0 };
    girara_setting_get(zathura->ui.session, index_settings[s], &tmp_value);
    if (tmp_value != NULL) {
      gdk_rgba_parse(&rgba, tmp_value);
      g_free(tmp_value);
    }

    char* color = gdk_rgba_to_string(&rgba);
    girara_template_set_variable_value(csstemplate,
        index_settings[s], color);
    g_free(color);
  }

  char* css = g_strdup_printf("%s\n%s", girara_template_get_base(csstemplate), CSS_TEMPLATE_INDEX);
  girara_template_set_base(csstemplate, css);
  g_free(css);

293
294
295
296
297
298
299
300
301
302
  /* Shortcut helpers */
  zathura->shortcut.mouse.x = 0;
  zathura->shortcut.mouse.y = 0;

  zathura->shortcut.toggle_page_mode.pages = 2;

  zathura->shortcut.toggle_presentation_mode.pages = 1;
  zathura->shortcut.toggle_presentation_mode.first_page_column_list = NULL;
  zathura->shortcut.toggle_presentation_mode.zoom = 1.0;

303
  /* Start D-Bus service */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
304
305
306
307
  bool dbus = true;
  girara_setting_get(zathura->ui.session, "dbus-service", &dbus);
  if (dbus == true) {
    zathura->dbus = zathura_dbus_new(zathura);
308
309
  }

310
  return true;
Moritz Lipp's avatar
Moritz Lipp committed
311
312
313

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
314
  if (zathura->ui.page_widget != NULL) {
315
    g_object_unref(zathura->ui.page_widget);
316
317
  }

318
  return false;
Moritz Lipp's avatar
Moritz Lipp committed
319
320
321
322
323
324
325
326
327
}

void
zathura_free(zathura_t* zathura)
{
  if (zathura == NULL) {
    return;
  }

Moritz Lipp's avatar
Moritz Lipp committed
328
  document_close(zathura, false);
329

330
331
332
333
334
335
336
#ifdef G_OS_UNIX
  if (zathura->signals.sigterm > 0) {
    g_source_remove(zathura->signals.sigterm);
    zathura->signals.sigterm = 0;
  }
#endif

337
  /* stop D-Bus */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
338
339
340
  if (zathura->dbus != NULL) {
    g_object_unref(zathura->dbus);
    zathura->dbus = NULL;
341
342
  }

Moritz Lipp's avatar
Moritz Lipp committed
343
344
345
346
  if (zathura->ui.session != NULL) {
    girara_session_destroy(zathura->ui.session);
  }

347
348
349
  /* shortcut */
  g_free(zathura->shortcut.toggle_presentation_mode.first_page_column_list);

350
351
352
353
354
355
  /* stdin support */
  if (zathura->stdin_support.file != NULL) {
    g_unlink(zathura->stdin_support.file);
    g_free(zathura->stdin_support.file);
  }

356
357
358
  /* bookmarks */
  girara_list_free(zathura->bookmarks.bookmarks);

359
  /* database */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
360
361
362
  if (zathura->database != NULL) {
    g_object_unref(G_OBJECT(zathura->database));
  }
363

364
  /* free print settings */
Moritz Lipp's avatar
Moritz Lipp committed
365
  if (zathura->print.settings != NULL) {
366
367
368
369
370
371
    g_object_unref(zathura->print.settings);
  }

  if (zathura->print.page_setup != NULL) {
    g_object_unref(zathura->print.page_setup);
  }
372

Moritz Lipp's avatar
Moritz Lipp committed
373
  /* free registered plugins */
Moritz Lipp's avatar
Moritz Lipp committed
374
  zathura_plugin_manager_free(zathura->plugins.manager);
375
376
377
378

  /* free config variables */
  g_free(zathura->config.config_dir);
  g_free(zathura->config.data_dir);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
379
  g_free(zathura->config.cache_dir);
Moritz Lipp's avatar
Moritz Lipp committed
380

381
382
383
384
385
386
387
388
389
  /* free jumplist */
  if (zathura->jumplist.list != NULL) {
    girara_list_free(zathura->jumplist.list);
  }

  if (zathura->jumplist.cur != NULL) {
    girara_list_iterator_free(zathura->jumplist.cur);
  }

390
  g_free(zathura);
391
392
}

393
#ifdef GDK_WINDOWING_X11
394
395
396
397
398
399
400
void
zathura_set_xid(zathura_t* zathura, Window xid)
{
  g_return_if_fail(zathura != NULL);

  zathura->ui.session->gtk.embed = xid;
}
401
#endif
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419

void
zathura_set_config_dir(zathura_t* zathura, const char* dir)
{
  g_return_if_fail(zathura != NULL);

  if (dir != NULL) {
    zathura->config.config_dir = g_strdup(dir);
  } else {
    gchar* path = girara_get_xdg_path(XDG_CONFIG);
    zathura->config.config_dir = g_build_filename(path, "zathura", NULL);
    g_free(path);
  }
}

void
zathura_set_data_dir(zathura_t* zathura, const char* dir)
{
Sebastian Ramacher's avatar
Sebastian Ramacher committed
420
421
  g_return_if_fail(zathura != NULL);

422
423
424
425
426
427
428
  if (dir != NULL) {
    zathura->config.data_dir = g_strdup(dir);
  } else {
    gchar* path = girara_get_xdg_path(XDG_DATA);
    zathura->config.data_dir = g_build_filename(path, "zathura", NULL);
    g_free(path);
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
429
}
430

Sebastian Ramacher's avatar
Sebastian Ramacher committed
431
432
433
void
zathura_set_cache_dir(zathura_t* zathura, const char* dir)
{
434
  g_return_if_fail(zathura != NULL);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
435
436
437
438
439
440
441
442

  if (dir != NULL) {
    zathura->config.cache_dir = g_strdup(dir);
  } else {
    gchar* path = girara_get_xdg_path(XDG_CACHE);
    zathura->config.cache_dir = g_build_filename(path, "zathura", NULL);
    g_free(path);
  }
443
444
445
446
447
448
449
450
451
452
453
}

void
zathura_set_plugin_dir(zathura_t* zathura, const char* dir)
{
  g_return_if_fail(zathura != NULL);
  g_return_if_fail(zathura->plugins.manager != NULL);

  if (dir != NULL) {
    girara_list_t* paths = girara_split_path_array(dir);
    GIRARA_LIST_FOREACH(paths, char*, iter, path)
Moritz Lipp's avatar
Moritz Lipp committed
454
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
455
456
457
458
459
460
    GIRARA_LIST_FOREACH_END(paths, char*, iter, path);
    girara_list_free(paths);
  } else {
#ifdef ZATHURA_PLUGINDIR
    girara_list_t* paths = girara_split_path_array(ZATHURA_PLUGINDIR);
    GIRARA_LIST_FOREACH(paths, char*, iter, path)
Moritz Lipp's avatar
Moritz Lipp committed
461
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
    GIRARA_LIST_FOREACH_END(paths, char*, iter, path);
    girara_list_free(paths);
#endif
  }

}

void
zathura_set_argv(zathura_t* zathura, char** argv)
{
  g_return_if_fail(zathura != NULL);

  zathura->global.arguments = argv;
}

477
static gchar*
Sebastian Ramacher's avatar
Sebastian Ramacher committed
478
prepare_document_open_from_stdin(const char* path)
479
{
480
481
482
483
484
485
486
487
488
489
490
491
492
493
  int infileno = -1;
  if (g_strcmp0(path, "-") == 0) {
    infileno = fileno(stdin);
  } else if (g_str_has_prefix(path, "/proc/self/fd/") == true) {
    char* begin = g_strrstr(path, "/") + 1;
    gint64 temp = g_ascii_strtoll(begin, NULL, 0);
    if (temp > INT_MAX || temp < 0) {
      return NULL;
    }
    infileno = (int) temp;
  } else {
    return NULL;
  }

494
495
496
  GError* error = NULL;
  gchar* file = NULL;
  gint handle = g_file_open_tmp("zathura.stdin.XXXXXX", &file, &error);
Moritz Lipp's avatar
Moritz Lipp committed
497
  if (handle == -1) {
498
499
500
501
    if (error != NULL) {
      girara_error("Can not create temporary file: %s", error->message);
      g_error_free(error);
    }
502
503
504
    return NULL;
  }

505
506
507
  // read and dump to temporary file
  if (infileno == -1) {
    girara_error("Can not read from file descriptor.");
508
509
510
511
512
513
514
515
    close(handle);
    g_unlink(file);
    g_free(file);
    return NULL;
  }

  char buffer[BUFSIZ];
  ssize_t count = 0;
516
  while ((count = read(infileno, buffer, BUFSIZ)) > 0) {
Moritz Lipp's avatar
Moritz Lipp committed
517
    if (write(handle, buffer, count) != count) {
518
519
520
521
522
523
524
      girara_error("Can not write to temporary file: %s", file);
      close(handle);
      g_unlink(file);
      g_free(file);
      return NULL;
    }
  }
Moritz Lipp's avatar
Moritz Lipp committed
525

526
527
  close(handle);

Moritz Lipp's avatar
Moritz Lipp committed
528
  if (count != 0) {
529
    girara_error("Can not read from file descriptor.");
530
531
532
533
534
535
536
537
    g_unlink(file);
    g_free(file);
    return NULL;
  }

  return file;
}

Lukas K.'s avatar
Lukas K. committed
538
static gchar*
Sebastian Ramacher's avatar
Sebastian Ramacher committed
539
prepare_document_open_from_gfile(GFile* source)
Lukas K.'s avatar
Lukas K. committed
540
541
{
  gchar* file = NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
542
  GFileIOStream* iostream = NULL;
Lukas K.'s avatar
Lukas K. committed
543
544
545
  GError* error = NULL;

  GFile *tmpfile = g_file_new_tmp("zathura.gio.XXXXXX", &iostream, &error);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
546
  if (tmpfile == NULL) {
Lukas K.'s avatar
Lukas K. committed
547
548
549
550
551
552
553
554
    if (error != NULL) {
      girara_error("Can not create temporary file: %s", error->message);
      g_error_free(error);
    }
    return NULL;
  }

  gboolean rc = g_file_copy(source, tmpfile, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
555
  if (rc == FALSE) {
Lukas K.'s avatar
Lukas K. committed
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
    if (error != NULL) {
      girara_error("Can not copy to temporary file: %s", error->message);
      g_error_free(error);
    }
    g_object_unref(iostream);
    g_object_unref(tmpfile);
    return NULL;
  }

  file = g_file_get_path(tmpfile);
  g_object_unref(iostream);
  g_object_unref(tmpfile);

  return file;
}

572
static gboolean
Moritz Lipp's avatar
Update    
Moritz Lipp committed
573
574
575
576
document_info_open(gpointer data)
{
  zathura_document_info_t* document_info = data;
  g_return_val_if_fail(document_info != NULL, FALSE);
Lukas K.'s avatar
Lukas K. committed
577
  char* uri = NULL;
Moritz Lipp's avatar
Update    
Moritz Lipp committed
578

579
  if (document_info->zathura != NULL && document_info->path != NULL) {
580
    char* file = NULL;
581
582
    if (g_strcmp0(document_info->path, "-") == 0 ||
        g_str_has_prefix(document_info->path, "/proc/self/fd/") == true) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
583
      file = prepare_document_open_from_stdin(document_info->path);
584
585
      if (file == NULL) {
        girara_notify(document_info->zathura->ui.session, GIRARA_ERROR,
586
                      _("Could not read file from stdin and write it to a temporary file."));
587
588
      } else {
        document_info->zathura->stdin_support.file = g_strdup(file);
589
590
      }
    } else {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
591
592
593
594
      GFile* gf = g_file_new_for_commandline_arg(document_info->path);
      if (g_file_is_native(gf) == TRUE) {
        /* file was given as a native path */
        file = g_file_get_path(gf);
Lukas K.'s avatar
Lukas K. committed
595
596
      }
      else {
597
        /* copy file with GIO */
Lukas K.'s avatar
Lukas K. committed
598
        uri = g_file_get_uri(gf);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
599
        file = prepare_document_open_from_gfile(gf);
600
601
602
        if (file == NULL) {
          girara_notify(document_info->zathura->ui.session, GIRARA_ERROR,
                        _("Could not read file from GIO and copy it to a temporary file."));
Lukas K.'s avatar
Lukas K. committed
603
        } else {
604
          document_info->zathura->stdin_support.file = g_strdup(file);
Lukas K.'s avatar
Lukas K. committed
605
606
607
        }
      }
      g_object_unref(gf);
608
609
610
    }

    if (file != NULL) {
611
      if (document_info->synctex != NULL) {
Lukas K.'s avatar
Lukas K. committed
612
        document_open_synctex(document_info->zathura, file, uri, 
613
614
                              document_info->password, document_info->synctex);
      } else {
Lukas K.'s avatar
Lukas K. committed
615
        document_open(document_info->zathura, file, uri, document_info->password,
616
617
                      document_info->page_number);
      }
618
      g_free(file);
Lukas K.'s avatar
Lukas K. committed
619
      g_free(uri);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
620
621
622
623
624
625
626
627
628
629
630
631

      if (document_info->mode != NULL) {
        if (g_strcmp0(document_info->mode, "presentation") == 0) {
          sc_toggle_presentation(document_info->zathura->ui.session, NULL, NULL,
                                 0);
        } else if (g_strcmp0(document_info->mode, "fullscreen") == 0) {
          sc_toggle_fullscreen(document_info->zathura->ui.session, NULL, NULL,
                               0);
        } else {
          girara_error("Unknown mode: %s", document_info->mode);
        }
      }
632
    }
Moritz Lipp's avatar
Update    
Moritz Lipp committed
633
634
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
635
  free_document_info(document_info);
Moritz Lipp's avatar
Update    
Moritz Lipp committed
636
  return FALSE;
637
638
}

Lukas K.'s avatar
Lukas K. committed
639
640
char*
get_formatted_filename(zathura_t* zathura, bool statusbar)
641
642
{
  bool basename_only = false;
Lukas K.'s avatar
Lukas K. committed
643
  const char* file_path = zathura_document_get_uri(zathura->document);
644
  if (file_path == NULL) {
Lukas K.'s avatar
Lukas K. committed
645
646
    file_path = zathura_document_get_path(zathura->document);
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
647
  if (statusbar == true) {
648
    girara_setting_get(zathura->ui.session, "statusbar-basename", &basename_only);
Lukas K.'s avatar
Lukas K. committed
649
650
  } else {
    girara_setting_get(zathura->ui.session, "window-title-basename", &basename_only);
651
652
  }

653
654
  if (basename_only == false) {
    bool home_tilde = false;
655
656
657
658
659
660
    if (statusbar) {
      girara_setting_get(zathura->ui.session, "statusbar-home-tilde", &home_tilde);
    } else {
      girara_setting_get(zathura->ui.session, "window-title-home-tilde", &home_tilde);
    }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
661
    const size_t file_path_len = file_path ? strlen(file_path) : 0;
662

Sebastian Ramacher's avatar
Sebastian Ramacher committed
663
664
665
    if (home_tilde == true) {
      char* home = girara_get_home_directory(NULL);
      const size_t home_len = home ? strlen(home) : 0;
666

Sidharth Kapur's avatar
Sidharth Kapur committed
667
668
669
670
      if (home_len > 1
          && file_path_len >= home_len
          && g_str_has_prefix(file_path, home)
          && (!file_path[home_len] || file_path[home_len] == '/')) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
671
        g_free(home);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
672
        return g_strdup_printf("~%s", &file_path[home_len]);
673
      } else {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
674
        g_free(home);
675
        return g_strdup(file_path);
676
677
      }
    } else {
678
      return g_strdup(file_path);
679
680
    }
  } else {
681
    const char* basename = zathura_document_get_basename(zathura->document);
682
    return g_strdup(basename);
683
684
685
  }
}

686
687
688
689
690
691
692
693
694
695
static gboolean
document_open_password_dialog(gpointer data)
{
  zathura_password_dialog_info_t* password_dialog_info = data;

  girara_dialog(password_dialog_info->zathura->ui.session, _("Enter password:"), true, NULL,
                (girara_callback_inputbar_activate_t) cb_password_dialog, password_dialog_info);
  return FALSE;
}

Moritz Lipp's avatar
Moritz Lipp committed
696
bool
Lukas K.'s avatar
Lukas K. committed
697
document_open(zathura_t* zathura, const char* path, const char* uri, const char* password,
698
              int page_number)
Moritz Lipp's avatar
Moritz Lipp committed
699
{
700
  if (zathura == NULL || zathura->plugins.manager == NULL || path == NULL) {
701
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
702
703
  }

704
  gchar* file_uri = NULL;
705
  zathura_error_t error = ZATHURA_ERROR_OK;
Lukas K.'s avatar
Lukas K. committed
706
  zathura_document_t* document = zathura_document_open(zathura->plugins.manager, path, uri, password, &error);
Moritz Lipp's avatar
Moritz Lipp committed
707

Moritz Lipp's avatar
Moritz Lipp committed
708
  if (document == NULL) {
709
    if (error == ZATHURA_ERROR_INVALID_PASSWORD) {
710
      girara_debug("Invalid or no password.");
711
712
713
      zathura_password_dialog_info_t* password_dialog_info = malloc(sizeof(zathura_password_dialog_info_t));
      if (password_dialog_info != NULL) {
        password_dialog_info->zathura = zathura;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
714
        password_dialog_info->path = g_strdup(path);
Lukas K.'s avatar
Lukas K. committed
715
        password_dialog_info->uri = g_strdup(uri);
716

Sebastian Ramacher's avatar
Sebastian Ramacher committed
717
        if (password_dialog_info->path != NULL) {
718
          gdk_threads_add_idle(document_open_password_dialog, password_dialog_info);
719
720
721
722
723
724
725
          goto error_out;
        } else {
          free(password_dialog_info);
        }
      }
      goto error_out;
    }
Moritz Lipp's avatar
Moritz Lipp committed
726
727
728
    if (error == ZATHURA_ERROR_OK ) {
      girara_notify(zathura->ui.session, GIRARA_ERROR, _("Unsupported file type. Please install the necessary plugin."));
    }
729
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
730
  }
Lukas K.'s avatar
Lukas K. committed
731
732
  
  zathura->document = document;
Moritz Lipp's avatar
Moritz Lipp committed
733

734
735
  const char* file_path        = zathura_document_get_path(document);
  unsigned int number_of_pages = zathura_document_get_number_of_pages(document);
736

737
738
739
740
741
742
  if (number_of_pages == 0) {
    girara_notify(zathura->ui.session, GIRARA_WARNING,
        _("Document does not contain any pages"));
    goto error_free;
  }

743
  /* read history file */
744
745
746
747
748
749
  zathura_fileinfo_t file_info = {
    .current_page = 0,
    .page_offset = 0,
    .scale = 1,
    .rotation = 0,
    .pages_per_row = 0,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
750
    .first_page_column_list = NULL,
751
752
753
    .position_x = 0,
    .position_y = 0
  };
754
755
756
757
  bool known_file = false;
  if (zathura->database != NULL) {
    known_file = zathura_db_get_fileinfo(zathura->database, file_path, &file_info);
  }
758

Moritz Lipp's avatar
Moritz Lipp committed
759
  /* set page offset */
760
  zathura_document_set_page_offset(document, file_info.page_offset);
761

762
  /* check for valid scale value */
763
764
765
  if (file_info.scale <= DBL_EPSILON) {
    file_info.scale = 1;
  }
Moritz Lipp's avatar
Moritz Lipp committed
766
767
  zathura_document_set_scale(document,
      zathura_correct_scale_value(zathura->ui.session, file_info.scale));
768
769

  /* check current page number */
770
771
772
773
774
775
  /* if it wasn't specified on the command-line, get it from file_info */
  if (page_number == ZATHURA_PAGE_NUMBER_UNSPECIFIED)
    page_number = file_info.current_page;
  if (page_number < 0)
    page_number += number_of_pages;
  if ((unsigned)page_number > number_of_pages) {
776
777
    girara_warning("document info: '%s' has an invalid page number", file_path);
    zathura_document_set_current_page_number(document, 0);
778
  } else {
779
    zathura_document_set_current_page_number(document, page_number);
780
781
782
  }

  /* check for valid rotation */
783
  if (file_info.rotation % 90 != 0) {
784
785
786
    girara_warning("document info: '%s' has an invalid rotation", file_path);
    zathura_document_set_rotation(document, 0);
  } else {
787
    zathura_document_set_rotation(document, file_info.rotation % 360);
788
789
790
791
792
793
794
795
796
797
798
  }

  /* jump to first page if setting enabled */
  bool always_first_page = false;
  girara_setting_get(zathura->ui.session, "open-first-page", &always_first_page);
  if (always_first_page == true) {
    zathura_document_set_current_page_number(document, 0);
  }

  /* apply open adjustment */
  char* adjust_open = "best-fit";
799
  if (known_file == false && girara_setting_get(zathura->ui.session, "adjust-open", &(adjust_open)) == true) {
800
801
802
803
804
805
806
807
    if (g_strcmp0(adjust_open, "best-fit") == 0) {
      zathura_document_set_adjust_mode(document, ZATHURA_ADJUST_BESTFIT);
    } else if (g_strcmp0(adjust_open, "width") == 0) {
      zathura_document_set_adjust_mode(document, ZATHURA_ADJUST_WIDTH);
    } else {
      zathura_document_set_adjust_mode(document, ZATHURA_ADJUST_NONE);
    }
    g_free(adjust_open);
808
809
  } else {
    zathura_document_set_adjust_mode(document, ZATHURA_ADJUST_NONE);
810
811
  }

812
813
814
815
816
  /* initialize bisect state */
  zathura->bisect.start = 0;
  zathura->bisect.last_jump = zathura_document_get_current_page_number(document);
  zathura->bisect.end = number_of_pages - 1;

817
  /* update statusbar */
Lukas K.'s avatar
Lukas K. committed
818
  char* filename = get_formatted_filename(zathura, true);
819
820
  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, filename);
  g_free(filename);
821

Moritz Lipp's avatar
Moritz Lipp committed
822
  /* install file monitor */
823
  file_uri = g_filename_to_uri(file_path, NULL, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
824
825
826
827
828
  if (file_uri == NULL) {
    goto error_free;
  }

  if (zathura->file_monitor.file == NULL) {
829
830
831
832
    zathura->file_monitor.file = g_file_new_for_uri(file_uri);
    if (zathura->file_monitor.file == NULL) {
      goto error_free;
    }
Moritz Lipp's avatar
Moritz Lipp committed
833
834
835
  }

  if (zathura->file_monitor.monitor == NULL) {
836
837
838
839
840
    zathura->file_monitor.monitor = g_file_monitor_file(zathura->file_monitor.file, G_FILE_MONITOR_NONE, NULL, NULL);
    if (zathura->file_monitor.monitor == NULL) {
      goto error_free;
    }
    g_signal_connect(G_OBJECT(zathura->file_monitor.monitor), "changed", G_CALLBACK(cb_file_monitor), zathura->ui.session);
Moritz Lipp's avatar
Moritz Lipp committed
841
842
843
  }

  if (zathura->file_monitor.file_path == NULL) {
844
    zathura->file_monitor.file_path = g_strdup(file_path);
845
846
847
    if (zathura->file_monitor.file_path == NULL) {
      goto error_free;
    }
Moritz Lipp's avatar
Moritz Lipp committed
848
849
  }

850
  if (password != NULL) {
851
    g_free(zathura->file_monitor.password);
852
    zathura->file_monitor.password = g_strdup(password);
Moritz Lipp's avatar
Moritz Lipp committed
853
854
855
856
857
    if (zathura->file_monitor.password == NULL) {
      goto error_free;
    }
  }

Moritz Lipp's avatar
Moritz Lipp committed
858
859
860
861
862
863
  /* create marks list */
  zathura->global.marks = girara_list_new2((girara_free_function_t) mark_free);
  if (zathura->global.marks == NULL) {
    goto error_free;
  }

864
865
866
867
868
869
870
871
872
  /* page cache size */
  int cache_size = 0;
  girara_setting_get(zathura->ui.session, "page-cache-size", &cache_size);
  if (cache_size <= 0) {
    girara_warning("page-cache-size is not positive, using %d instead",
        ZATHURA_PAGE_CACHE_DEFAULT_SIZE);
    cache_size = ZATHURA_PAGE_CACHE_DEFAULT_SIZE;
  }

873
  /* threads */
874
  zathura->sync.render_thread = zathura_renderer_new(cache_size);
875
876
877
878
879

  if (zathura->sync.render_thread == NULL) {
    goto error_free;
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
880
  /* set up recolor info in ZathuraRenderer */
881
  char* recolor_dark  = NULL;
882
883
884
885
886
887
888
889
  char* recolor_light = NULL;
  girara_setting_get(zathura->ui.session, "recolor-darkcolor", &recolor_dark);
  girara_setting_get(zathura->ui.session, "recolor-lightcolor", &recolor_light);
  zathura_renderer_set_recolor_colors_str(zathura->sync.render_thread,
      recolor_light, recolor_dark);
  g_free(recolor_dark);
  g_free(recolor_light);

890
891
892
893
894
  bool recolor = false;
  girara_setting_get(zathura->ui.session, "recolor", &recolor);
  zathura_renderer_enable_recolor(zathura->sync.render_thread, recolor);
  girara_setting_get(zathura->ui.session, "recolor-keephue", &recolor);
  zathura_renderer_enable_recolor_hue(zathura->sync.render_thread, recolor);
895
896
  girara_setting_get(zathura->ui.session, "recolor-reverse-video", &recolor);
  zathura_renderer_enable_recolor_reverse_video(zathura->sync.render_thread, recolor);
897

898
899
900
901
902
903
  /* get view port size */
  GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(
                 GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
  GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(
                 GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));

904
  const unsigned int view_width = (unsigned int)floor(gtk_adjustment_get_page_size(hadjustment));
905
  zathura_document_set_viewport_width(zathura->document, view_width);
906
  const unsigned int view_height = (unsigned int)floor(gtk_adjustment_get_page_size(vadjustment));
907
908
  zathura_document_set_viewport_height(zathura->document, view_height);

909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
  /* create blank pages */
  zathura->pages = calloc(number_of_pages, sizeof(GtkWidget*));
  if (zathura->pages == NULL) {
    goto error_free;
  }

  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
    zathura_page_t* page = zathura_document_get_page(document, page_id);
    if (page == NULL) {
      goto error_free;
    }

    GtkWidget* page_widget = zathura_page_widget_new(zathura, page);
    if (page_widget == NULL) {
      goto error_free;
    }

akbjker's avatar
akbjker committed
926
    g_object_ref(page_widget);
927
928
    zathura->pages[page_id] = page_widget;

929
930
931
    gtk_widget_set_halign(page_widget, GTK_ALIGN_CENTER);
    gtk_widget_set_valign(page_widget, GTK_ALIGN_CENTER);

932
933
934
    g_signal_connect(G_OBJECT(page_widget), "text-selected",
        G_CALLBACK(cb_page_widget_text_selected), zathura);
    g_signal_connect(G_OBJECT(page_widget), "image-selected",
935
        G_CALLBACK(cb_page_widget_image_selected), zathura);
936
937
938
939
    g_signal_connect(G_OBJECT(page_widget), "enter-link",
        G_CALLBACK(cb_page_widget_link), (gpointer) true);
    g_signal_connect(G_OBJECT(page_widget), "leave-link",
        G_CALLBACK(cb_page_widget_link), (gpointer) false);
940
941
    g_signal_connect(G_OBJECT(page_widget), "scaled-button-release",
        G_CALLBACK(cb_page_widget_scaled_button_release), zathura);
942
943
  }

944
  /* view mode */
945
  unsigned int pages_per_row = 1;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
946
  char* first_page_column_list = NULL;
947
948
949
950
  unsigned int page_padding = 1;

  girara_setting_get(zathura->ui.session, "page-padding", &page_padding);

951
952
953
954
955
  if (file_info.pages_per_row > 0) {
    pages_per_row = file_info.pages_per_row;
  } else {
    girara_setting_get(zathura->ui.session, "pages-per-row", &pages_per_row);
  }
Moritz Lipp's avatar
Moritz Lipp committed
956

957
  /* read first_page_column list */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
958
  if (file_info.first_page_column_list != NULL && strcmp(file_info.first_page_column_list, "")) {
959
    first_page_column_list = file_info.first_page_column_list;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
960
    file_info.first_page_column_list = NULL;
961
  } else {
962
    girara_setting_get(zathura->ui.session, "first-page-column", &first_page_column_list);
963
964
  }

965
966
967
  /* find value for first_page_column */
  unsigned int first_page_column = find_first_page_column(first_page_column_list, pages_per_row);

Moritz Lipp's avatar
Moritz Lipp committed
968
  girara_setting_set(zathura->ui.session, "pages-per-row", &pages_per_row);
969
  girara_setting_set(zathura->ui.session, "first-page-column", first_page_column_list);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
970
  g_free(file_info.first_page_column_list);
971
  g_free(first_page_column_list);
972
973
974

  page_widget_set_mode(zathura, page_padding, pages_per_row, first_page_column);
  zathura_document_set_page_layout(zathura->document, page_padding, pages_per_row, first_page_column);
Moritz Lipp's avatar
Moritz Lipp committed
975

976
  girara_set_view(zathura->ui.session, zathura->ui.page_widget);
Moritz Lipp's avatar
Moritz Lipp committed
977

Moritz Lipp's avatar
Moritz Lipp committed
978

979
  /* bookmarks */
980
981
  if (zathura->database != NULL) {
    if (zathura_bookmarks_load(zathura, file_path) == false) {
982
      girara_debug("Failed to load bookmarks.");
983
    }
984

985
986
987
988
    /* jumplist */
    if (zathura_jumplist_load(zathura, file_path) == false) {
      zathura->jumplist.list = girara_list_new2(g_free);
    }
989
990
  }

Moritz Lipp's avatar
Moritz Lipp committed
991
  /* update title */
Lukas K.'s avatar
Lukas K. committed
992
  char* formatted_filename = get_formatted_filename(zathura, false);
993
994
  girara_set_window_title(zathura->ui.session, formatted_filename);
  g_free(formatted_filename);
Moritz Lipp's avatar
Moritz Lipp committed
995

996
  g_free(file_uri);
Moritz Lipp's avatar
Moritz Lipp committed
997

998
  /* adjust_view */
999
  adjust_view(zathura);
1000
1001
1002
1003
1004
1005
1006
1007
1008
  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
    /* set widget size */
    zathura_page_t* page = zathura_document_get_page(document, page_id);
    unsigned int page_height = 0;
    unsigned int page_width  = 0;

    /* adjust_view calls render_all in some cases and render_all calls
     * gtk_widget_set_size_request. To be sure that it's really called, do it
     * here once again. */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
1009
1010
    const double height = zathura_page_get_height(page);
    const double width = zathura_page_get_width(page);
1011
1012
1013
1014
1015
1016
    page_calc_height_width(zathura->document, height, width