zathura.c 44.2 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>
8
#include <math.h>
9
#include <string.h>
Moritz Lipp's avatar
Moritz Lipp committed
10

11 12 13 14 15
#include <girara/datastructures.h>
#include <girara/utils.h>
#include <girara/session.h>
#include <girara/statusbar.h>
#include <girara/settings.h>
16
#include <girara/shortcuts.h>
17
#include <girara/template.h>
18
#include <glib/gstdio.h>
19
#include <glib/gi18n.h>
20

Moritz Lipp's avatar
Moritz Lipp committed
21 22
#ifdef G_OS_UNIX
#include <glib-unix.h>
23
#include <gio/gunixinputstream.h>
Moritz Lipp's avatar
Moritz Lipp committed
24 25
#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"
44
#include "css-definitions.h"
45
#include "synctex.h"
46
#include "content-type.h"
Moritz Lipp's avatar
Moritz Lipp committed
47

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

57

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

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

Sebastian Ramacher's avatar
Sebastian Ramacher committed
64 65 66 67 68 69 70 71 72 73 74 75 76 77
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
78
/* function implementation */
Moritz Lipp's avatar
Moritz Lipp committed
79
zathura_t*
80
zathura_create(void)
81
{
82 83 84 85
  zathura_t* zathura = g_try_malloc0(sizeof(zathura_t));
  if (zathura == NULL) {
    return NULL;
  }
86

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

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

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

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

107 108 109
  /* MIME type detection */
  zathura->content_type_context = zathura_content_type_new();

110 111 112 113 114 115 116 117 118 119
  zathura->ui.session->global.data = zathura;

  return zathura;

error_out:

  zathura_free(zathura);

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

121 122
static void
create_directories(zathura_t* zathura)
123
{
124
  static const unsigned int mode = 0700;
125

126 127 128
  if (g_mkdir_with_parents(zathura->config.config_dir, mode) == -1) {
    girara_error("Could not create '%s': %s", zathura->config.config_dir,
                 strerror(errno));
129 130
  }

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

137 138 139
static bool
init_ui(zathura_t* zathura)
{
Sebastian Ramacher's avatar
Sebastian Ramacher committed
140
  if (girara_session_init(zathura->ui.session, "zathura") == false) {
141
    return false;
142 143 144
  }

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

148
  /* zathura signals */
149 150 151
  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);
152 153 154 155

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

156
  /* page view */
157
  zathura->ui.page_widget = gtk_grid_new();
158 159
  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
160
  if (zathura->ui.page_widget == NULL) {
161
    return false;
162 163
  }

164 165
  g_signal_connect(G_OBJECT(zathura->ui.session->gtk.window), "size-allocate",
                   G_CALLBACK(cb_view_resized), zathura);
166

167
  GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(
168
                 GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
169 170 171

  /* Connect hadjustment signals */
  g_signal_connect(G_OBJECT(hadjustment), "value-changed",
172
                   G_CALLBACK(cb_view_hadjustment_value_changed), zathura);
173
  g_signal_connect(G_OBJECT(hadjustment), "changed",
174
                   G_CALLBACK(cb_view_hadjustment_changed), zathura);
175 176

  GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(
177
                 GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
178 179 180

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

185
  /* page view alignment */
186 187
  gtk_widget_set_halign(zathura->ui.page_widget, GTK_ALIGN_CENTER);
  gtk_widget_set_valign(zathura->ui.page_widget, GTK_ALIGN_CENTER);
188

189 190 191 192
  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);
193

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

196
  /* statusbar */
197 198
  zathura->ui.statusbar.file =
    girara_statusbar_item_add(zathura->ui.session, TRUE, TRUE, TRUE, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
199
  if (zathura->ui.statusbar.file == NULL) {
200
    return false;
Moritz Lipp's avatar
Moritz Lipp committed
201 202
  }

203 204
  zathura->ui.statusbar.buffer =
    girara_statusbar_item_add(zathura->ui.session, FALSE, FALSE, FALSE, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
205
  if (zathura->ui.statusbar.buffer == NULL) {
206
    return false;
Moritz Lipp's avatar
Moritz Lipp committed
207 208
  }

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

215 216
  girara_statusbar_item_set_text(zathura->ui.session,
                                 zathura->ui.statusbar.file, _("[No name]"));
Moritz Lipp's avatar
Moritz Lipp committed
217

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

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
  return true;
}

static void
init_css(zathura_t* zathura)
{
  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);
}

static void
init_database(zathura_t* zathura)
{
263 264 265 266
  char* database = NULL;
  girara_setting_get(zathura->ui.session, "database", &database);

  if (g_strcmp0(database, "plain") == 0) {
267
    girara_debug("Using plain database backend.");
268 269 270
    zathura->database = zathura_plaindatabase_new(zathura->config.data_dir);
#ifdef WITH_SQLITE
  } else if (g_strcmp0(database, "sqlite") == 0) {
271
    girara_debug("Using sqlite database backend.");
272 273
    char* tmp =
      g_build_filename(zathura->config.data_dir, "bookmarks.sqlite", NULL);
274 275 276
    zathura->database = zathura_sqldatabase_new(tmp);
    g_free(tmp);
#endif
277
  } else if (g_strcmp0(database, "null") != 0) {
278 279 280
    girara_error("Database backend '%s' is not supported.", database);
  }

281
  if (zathura->database == NULL && g_strcmp0(database, "null") != 0) {
282 283 284 285 286 287
    girara_error(
      "Unable to initialize database. Bookmarks won't be available.");
  }
  else {
    g_object_set(G_OBJECT(zathura->ui.session->command_history), "io",
                 zathura->database, NULL);
288
  }
289
  g_free(database);
290
}
291

292 293 294
static void
init_jumplist(zathura_t* zathura)
{
295 296
  int jumplist_size = 20;
  girara_setting_get(zathura->ui.session, "jumplist-size", &jumplist_size);
297

298
  zathura->jumplist.max_size = jumplist_size < 0 ? 0 : jumplist_size;
299 300 301 302
  zathura->jumplist.list     = NULL;
  zathura->jumplist.size     = 0;
  zathura->jumplist.cur      = NULL;
}
303

304 305 306 307 308
static void
init_shortcut_helpers(zathura_t* zathura)
{
  zathura->shortcut.mouse.x = 0;
  zathura->shortcut.mouse.y = 0;
309

310
  zathura->shortcut.toggle_page_mode.pages = 2;
311

312 313 314 315
  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;
}
316

317 318 319 320 321 322
bool
zathura_init(zathura_t* zathura)
{
  if (zathura == NULL) {
    return false;
  }
323

324 325 326 327 328 329 330 331 332 333 334 335 336
  /* create zathura (config/data) directory */
  create_directories(zathura);

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

  /* configuration */
  config_load_default(zathura);
  config_load_files(zathura);

  /* UI */
  if (!init_ui(zathura)) {
    goto error_free;
337 338
  }

339 340
  /* database */
  init_database(zathura);
341

342 343 344 345
  /* bookmarks */
  zathura->bookmarks.bookmarks = girara_sorted_list_new2(
    (girara_compare_function_t)zathura_bookmarks_compare,
    (girara_free_function_t)zathura_bookmark_free);
346

347 348
  /* jumplist */
  init_jumplist(zathura);
349

350 351 352 353 354
  /* CSS for index mode */
  init_css(zathura);

  /* Shortcut helpers */
  init_shortcut_helpers(zathura);
355

356
  /* Start D-Bus service */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
357 358 359 360
  bool dbus = true;
  girara_setting_get(zathura->ui.session, "dbus-service", &dbus);
  if (dbus == true) {
    zathura->dbus = zathura_dbus_new(zathura);
361 362
  }

363
  return true;
Moritz Lipp's avatar
Moritz Lipp committed
364 365 366

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
367
  if (zathura->ui.page_widget != NULL) {
368
    g_object_unref(zathura->ui.page_widget);
369 370
  }

371
  return false;
Moritz Lipp's avatar
Moritz Lipp committed
372 373 374 375 376 377 378 379 380
}

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

Moritz Lipp's avatar
Moritz Lipp committed
381
  document_close(zathura, false);
382

383 384 385
  /* MIME type detection */
  zathura_content_type_free(zathura->content_type_context);

386 387 388 389 390 391 392
#ifdef G_OS_UNIX
  if (zathura->signals.sigterm > 0) {
    g_source_remove(zathura->signals.sigterm);
    zathura->signals.sigterm = 0;
  }
#endif

393
  /* stop D-Bus */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
394 395 396
  if (zathura->dbus != NULL) {
    g_object_unref(zathura->dbus);
    zathura->dbus = NULL;
397 398
  }

Moritz Lipp's avatar
Moritz Lipp committed
399 400 401 402
  if (zathura->ui.session != NULL) {
    girara_session_destroy(zathura->ui.session);
  }

403
  /* shortcut */
404 405 406
  if (zathura->shortcut.toggle_presentation_mode.first_page_column_list != NULL) {
    g_free(zathura->shortcut.toggle_presentation_mode.first_page_column_list);
  }
407

408 409 410 411 412 413
  /* stdin support */
  if (zathura->stdin_support.file != NULL) {
    g_unlink(zathura->stdin_support.file);
    g_free(zathura->stdin_support.file);
  }

414 415 416
  /* bookmarks */
  girara_list_free(zathura->bookmarks.bookmarks);

417
  /* database */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
418 419 420
  if (zathura->database != NULL) {
    g_object_unref(G_OBJECT(zathura->database));
  }
421

422
  /* free print settings */
Moritz Lipp's avatar
Moritz Lipp committed
423
  if (zathura->print.settings != NULL) {
424 425 426 427 428 429
    g_object_unref(zathura->print.settings);
  }

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

Moritz Lipp's avatar
Moritz Lipp committed
431
  /* free registered plugins */
Moritz Lipp's avatar
Moritz Lipp committed
432
  zathura_plugin_manager_free(zathura->plugins.manager);
433 434 435 436

  /* free config variables */
  g_free(zathura->config.config_dir);
  g_free(zathura->config.data_dir);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
437
  g_free(zathura->config.cache_dir);
438

439 440 441 442 443 444 445 446 447
  /* 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);
  }

448
  g_free(zathura);
449 450
}

451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
void
zathura_set_xid(zathura_t* zathura, Window xid)
{
  g_return_if_fail(zathura != NULL);

  zathura->ui.session->gtk.embed = xid;
}

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
476 477
  g_return_if_fail(zathura != NULL);

478 479 480 481 482 483 484
  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
485
}
486

Sebastian Ramacher's avatar
Sebastian Ramacher committed
487 488 489
void
zathura_set_cache_dir(zathura_t* zathura, const char* dir)
{
490
  g_return_if_fail(zathura != NULL);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
491 492 493 494 495 496 497 498

  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);
  }
499 500 501 502 503 504 505 506 507 508 509
}

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
510
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
511 512 513 514 515 516
    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
517
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
    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;
}

533
#ifdef G_OS_UNIX
534
static gchar*
Sebastian Ramacher's avatar
Sebastian Ramacher committed
535
prepare_document_open_from_stdin(const char* path)
536
{
537 538 539 540 541 542 543 544 545 546 547 548 549 550
  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;
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
551 552 553 554 555
  if (infileno == -1) {
    girara_error("Can not read from file descriptor.");
    return NULL;
  }

556 557 558 559 560 561 562 563 564 565 566
  GInputStream* input_stream = g_unix_input_stream_new(infileno, false);
  if (input_stream == NULL) {
    girara_error("Can not read from file descriptor.");
    return NULL;

  }

  GFileIOStream* iostream = NULL;
  GError*        error    = NULL;
  GFile* tmpfile = g_file_new_tmp("zathura.stdin.XXXXXX", &iostream, &error);
  if (tmpfile == NULL) {
567 568 569 570
    if (error != NULL) {
      girara_error("Can not create temporary file: %s", error->message);
      g_error_free(error);
    }
571
    g_object_unref(input_stream);
572 573 574
    return NULL;
  }

575 576 577 578 579 580 581 582 583
  const ssize_t count = g_output_stream_splice(
    g_io_stream_get_output_stream(G_IO_STREAM(iostream)), input_stream,
    G_OUTPUT_STREAM_SPLICE_NONE, NULL, &error);
  g_object_unref(input_stream);
  g_object_unref(iostream);
  if (count == -1) {
    if (error != NULL) {
      girara_error("Can not write to temporary file: %s", error->message);
      g_error_free(error);
584
    }
585 586
    g_file_delete(tmpfile, NULL, NULL);
    g_object_unref(tmpfile);
587 588 589
    return NULL;
  }

590 591 592
  char* file = g_file_get_path(tmpfile);
  g_object_unref(tmpfile);

593 594
  return file;
}
595
#endif
596

Lukas K.'s avatar
Lukas K. committed
597
static gchar*
Sebastian Ramacher's avatar
Sebastian Ramacher committed
598
prepare_document_open_from_gfile(GFile* source)
Lukas K.'s avatar
Lukas K. committed
599
{
600
  gchar*         file     = NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
601
  GFileIOStream* iostream = NULL;
602
  GError*        error    = NULL;
Lukas K.'s avatar
Lukas K. committed
603

604
  GFile* tmpfile = g_file_new_tmp("zathura.gio.XXXXXX", &iostream, &error);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
605
  if (tmpfile == NULL) {
Lukas K.'s avatar
Lukas K. committed
606 607 608 609 610 611 612
    if (error != NULL) {
      girara_error("Can not create temporary file: %s", error->message);
      g_error_free(error);
    }
    return NULL;
  }

613 614
  gboolean rc = g_file_copy(source, tmpfile, G_FILE_COPY_OVERWRITE, NULL, NULL,
                            NULL, &error);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
615
  if (rc == FALSE) {
Lukas K.'s avatar
Lukas K. committed
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
    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;
}

632
static gboolean
Moritz Lipp's avatar
Moritz Lipp committed
633 634 635 636
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
637
  char* uri = NULL;
Moritz Lipp's avatar
Moritz Lipp committed
638

639
  if (document_info->zathura != NULL && document_info->path != NULL) {
640
    char* file = NULL;
641 642
    if (g_strcmp0(document_info->path, "-") == 0 ||
        g_str_has_prefix(document_info->path, "/proc/self/fd/") == true) {
643
#ifdef G_OS_UNIX
Sebastian Ramacher's avatar
Sebastian Ramacher committed
644
      file = prepare_document_open_from_stdin(document_info->path);
645
#endif
646 647
      if (file == NULL) {
        girara_notify(document_info->zathura->ui.session, GIRARA_ERROR,
648
                      _("Could not read file from stdin and write it to a temporary file."));
649 650
      } else {
        document_info->zathura->stdin_support.file = g_strdup(file);
651 652
      }
    } else {
653 654 655 656
      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
657 658
      }
      else {
659
        /* copy file with GIO */
Lukas K.'s avatar
Lukas K. committed
660
        uri = g_file_get_uri(gf);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
661
        file = prepare_document_open_from_gfile(gf);
662 663 664
        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
665
        } else {
666
          document_info->zathura->stdin_support.file = g_strdup(file);
Lukas K.'s avatar
Lukas K. committed
667 668 669
        }
      }
      g_object_unref(gf);
670 671 672
    }

    if (file != NULL) {
673
      if (document_info->synctex != NULL) {
Lukas K.'s avatar
Lukas K. committed
674
        document_open_synctex(document_info->zathura, file, uri, 
675 676
                              document_info->password, document_info->synctex);
      } else {
Lukas K.'s avatar
Lukas K. committed
677
        document_open(document_info->zathura, file, uri, document_info->password,
678 679
                      document_info->page_number);
      }
680
      g_free(file);
Lukas K.'s avatar
Lukas K. committed
681
      g_free(uri);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
682 683 684 685 686 687 688 689 690 691 692 693

      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);
        }
      }
694
    }
Moritz Lipp's avatar
Moritz Lipp committed
695 696
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
697
  free_document_info(document_info);
Moritz Lipp's avatar
Moritz Lipp committed
698
  return FALSE;
699 700
}

Lukas K.'s avatar
Lukas K. committed
701 702
char*
get_formatted_filename(zathura_t* zathura, bool statusbar)
703 704
{
  bool basename_only = false;
Lukas K.'s avatar
Lukas K. committed
705
  const char* file_path = zathura_document_get_uri(zathura->document);
706
  if (file_path == NULL) {
Lukas K.'s avatar
Lukas K. committed
707 708
    file_path = zathura_document_get_path(zathura->document);
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
709
  if (statusbar == true) {
710
    girara_setting_get(zathura->ui.session, "statusbar-basename", &basename_only);
Lukas K.'s avatar
Lukas K. committed
711 712
  } else {
    girara_setting_get(zathura->ui.session, "window-title-basename", &basename_only);
713 714
  }

715 716
  if (basename_only == false) {
    bool home_tilde = false;
717 718 719 720 721 722
    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
723
    const size_t file_path_len = file_path ? strlen(file_path) : 0;
724

Sebastian Ramacher's avatar
Sebastian Ramacher committed
725 726 727
    if (home_tilde == true) {
      char* home = girara_get_home_directory(NULL);
      const size_t home_len = home ? strlen(home) : 0;
728

Sidharth Kapur's avatar
Sidharth Kapur committed
729 730 731 732
      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
733
        g_free(home);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
734
        return g_strdup_printf("~%s", &file_path[home_len]);
735
      } else {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
736
        g_free(home);
737
        return g_strdup(file_path);
738 739
      }
    } else {
740
      return g_strdup(file_path);
741 742
    }
  } else {
743
    const char* basename = zathura_document_get_basename(zathura->document);
744
    return g_strdup(basename);
745 746 747
  }
}

748 749 750 751 752 753
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,
754
                cb_password_dialog, password_dialog_info);
755 756 757
  return FALSE;
}

Moritz Lipp's avatar
Moritz Lipp committed
758
bool
Lukas K.'s avatar
Lukas K. committed
759
document_open(zathura_t* zathura, const char* path, const char* uri, const char* password,
760
              int page_number)
Moritz Lipp's avatar
Moritz Lipp committed
761
{
762
  if (zathura == NULL || zathura->plugins.manager == NULL || path == NULL) {
763
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
764 765
  }

766
  zathura_error_t error = ZATHURA_ERROR_OK;
767
  zathura_document_t* document = zathura_document_open(zathura, path, uri, password, &error);
Moritz Lipp's avatar
Moritz Lipp committed
768

Moritz Lipp's avatar
Moritz Lipp committed
769
  if (document == NULL) {
770
    if (error == ZATHURA_ERROR_INVALID_PASSWORD) {
771
      girara_debug("Invalid or no password.");
772 773 774
      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
775
        password_dialog_info->path = g_strdup(path);
Lukas K.'s avatar
Lukas K. committed
776
        password_dialog_info->uri = g_strdup(uri);
777

Sebastian Ramacher's avatar
Sebastian Ramacher committed
778
        if (password_dialog_info->path != NULL) {
779
          gdk_threads_add_idle(document_open_password_dialog, password_dialog_info);
780 781 782 783 784 785 786
          goto error_out;
        } else {
          free(password_dialog_info);
        }
      }
      goto error_out;
    }
Moritz Lipp's avatar
Moritz Lipp committed
787 788 789
    if (error == ZATHURA_ERROR_OK ) {
      girara_notify(zathura->ui.session, GIRARA_ERROR, _("Unsupported file type. Please install the necessary plugin."));
    }
790
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
791
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
792

793 794
  const char* file_path        = zathura_document_get_path(document);
  unsigned int number_of_pages = zathura_document_get_number_of_pages(document);
795

796 797 798 799 800 801
  if (number_of_pages == 0) {
    girara_notify(zathura->ui.session, GIRARA_WARNING,
        _("Document does not contain any pages"));
    goto error_free;
  }

802 803
  zathura->document = document;

804
  /* read history file */
805 806 807 808 809 810
  zathura_fileinfo_t file_info = {
    .current_page = 0,
    .page_offset = 0,
    .scale = 1,
    .rotation = 0,
    .pages_per_row = 0,
811
    .first_page_column_list = NULL,
812 813 814
    .position_x = 0,
    .position_y = 0
  };
815 816 817 818
  bool known_file = false;
  if (zathura->database != NULL) {
    known_file = zathura_db_get_fileinfo(zathura->database, file_path, &file_info);
  }
819

Moritz Lipp's avatar
Moritz Lipp committed
820
  /* set page offset */
821
  zathura_document_set_page_offset(document, file_info.page_offset);
822

823
  /* check for valid scale value */
824 825 826
  if (file_info.scale <= DBL_EPSILON) {
    file_info.scale = 1;
  }
Moritz Lipp's avatar
Moritz Lipp committed
827 828
  zathura_document_set_scale(document,
      zathura_correct_scale_value(zathura->ui.session, file_info.scale));
829 830

  /* check current page number */
831 832 833 834 835 836
  /* 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) {
837 838
    girara_warning("document info: '%s' has an invalid page number", file_path);
    zathura_document_set_current_page_number(document, 0);
839
  } else {
840
    zathura_document_set_current_page_number(document, page_number);
841 842 843
  }

  /* check for valid rotation */
844
  if (file_info.rotation % 90 != 0) {
845 846 847
    girara_warning("document info: '%s' has an invalid rotation", file_path);
    zathura_document_set_rotation(document, 0);
  } else {
848
    zathura_document_set_rotation(document, file_info.rotation % 360);
849 850 851 852 853 854 855 856 857 858 859
  }

  /* 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";
860
  if (known_file == false && girara_setting_get(zathura->ui.session, "adjust-open", &(adjust_open)) == true) {
861 862 863 864 865 866 867 868
    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);
869 870
  } else {
    zathura_document_set_adjust_mode(document, ZATHURA_ADJUST_NONE);
871 872
  }

873 874 875 876 877
  /* 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;

878
  /* update statusbar */
Lukas K.'s avatar
Lukas K. committed
879
  char* filename = get_formatted_filename(zathura, true);
880 881
  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, filename);
  g_free(filename);
882

Moritz Lipp's avatar
Moritz Lipp committed
883 884
  /* install file monitor */
  if (zathura->file_monitor.monitor == NULL) {
885 886 887 888 889 890
    char* filemonitor_backend = NULL;
    girara_setting_get(zathura->ui.session, "filemonitor", &filemonitor_backend);
    zathura_filemonitor_type_t type = ZATHURA_FILEMONITOR_GLIB;
#ifdef G_OS_UNIX
    if (g_strcmp0(filemonitor_backend, "signal") == 0) {
      type = ZATHURA_FILEMONITOR_SIGNAL;
891
    }
892
#endif
Moritz Lipp's avatar
Moritz Lipp committed
893

894 895
    zathura->file_monitor.monitor = zathura_filemonitor_new(file_path, type);
    if (zathura->file_monitor.monitor == NULL) {
896 897
      goto error_free;
    }
898 899
    g_signal_connect(G_OBJECT(zathura->file_monitor.monitor), "reload-file",
                     G_CALLBACK(cb_file_monitor), zathura->ui.session);
Moritz Lipp's avatar
Moritz Lipp committed
900 901
  }

902
  if (password != NULL) {
903
    g_free(zathura->file_monitor.password);
904
    zathura->file_monitor.password = g_strdup(password);
Moritz Lipp's avatar
Moritz Lipp committed
905 906 907 908 909
    if (zathura->file_monitor.password == NULL) {
      goto error_free;
    }
  }

Moritz Lipp's avatar
Moritz Lipp committed
910 911 912 913 914 915
  /* create marks list */
  zathura->global.marks = girara_list_new2((girara_free_function_t) mark_free);
  if (zathura->global.marks == NULL) {
    goto error_free;
  }

916 917 918 919 920 921 922 923 924
  /* 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;
  }

925
  /* threads */
926
  zathura->sync.render_thread = zathura_renderer_new(cache_size);
927 928 929 930 931

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

Sebastian Ramacher's avatar
Sebastian Ramacher committed
932
  /* set up recolor info in ZathuraRenderer */
933
  char* recolor_dark  = NULL;
934 935 936 937 938 939 940 941
  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);

942 943 944 945 946
  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);
947 948
  girara_setting_get(zathura->ui.session, "recolor-reverse-video", &recolor);
  zathura_renderer_enable_recolor_reverse_video(zathura->sync.render_thread, recolor);
949

950 951 952 953 954 955
  /* 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));

956
  const unsigned int view_width = (unsigned int)floor(gtk_adjustment_get_page_size(hadjustment));
957
  zathura_document_set_viewport_width(zathura->document, view_width);
958
  const unsigned int view_height = (unsigned int)floor(gtk_adjustment_get_page_size(vadjustment));
959 960
  zathura_document_set_viewport_height(zathura->document, view_height);

961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977
  /* 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
978
    g_object_ref(page_widget);
979 980
    zathura->pages[page_id] = page_widget;

981 982 983
    gtk_widget_set_halign(page_widget, GTK_ALIGN_CENTER);
    gtk_widget_set_valign(page_widget, GTK_ALIGN_CENTER);

984 985 986
    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",
987
        G_CALLBACK(cb_page_widget_image_selected), zathura);
988 989 990 991
    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);
992 993
    g_signal_connect(G_OBJECT(page_widget), "scaled-button-release",
        G_CALLBACK(cb_page_widget_scaled_button_release), zathura);
994 995
  }

996
  /* view mode */
997
  unsigned int pages_per_row = 1;
998
  char* first_page_column_list = NULL;
999 1000 1001 1002
  unsigned int page_padding = 1;

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

1003 1004 1005 1006 1007
  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
1008

1009
  /* read first_page_column list */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
1010
  if (file_info.first_page_column_list != NULL && *file_info.first_page_column_list != '\0') {
1011
    first_page_column_list = file_info.first_page_column_list;
1012
    file_info.first_page_column_list = NULL;
1013
  } else {
1014
    girara_setting_get(zathura->ui.session, "first-page-column", &first_page_column_list);
1015 1016
  }

1017 1018 1019
  /* 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
1020
  girara_setting_set(zathura->ui.session, "pages-per-row", &pages_per_row);
1021
  girara_setting_set(zathura->ui.session, "first-page-column", first_page_column_list);
1022
  g_free(file_info.first_page_column_list);
1023
  g_free(first_page_column_list);
1024 1025 1026

  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
1027

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

Moritz Lipp's avatar
Moritz Lipp committed
1030

1031
  /* bookmarks */
1032 1033
  if (zathura->database != NULL) {
    if (zathura_bookmarks_load(zathura, file_path) == false) {
1034
      girara_debug("Failed to load bookmarks.");
1035
    }
1036

1037 1038 1039 1040
    /* jumplist */
    if (zathura_jumplist_load(zathura, file_path) == false) {
      zathura->jumplist.list = girara_list_new2(g_free);
    }
1041 1042
  }

Moritz Lipp's avatar
Moritz Lipp committed
1043
  /* update title */
Lukas K.'s avatar
Lukas K. committed
1044
  char* formatted_filename = get_formatted_filename(zathura, false);
1045 1046
  girara_set_window_title(zathura->ui.session, formatted_filename);
  g_free(formatted_filename);
Moritz Lipp's avatar
Moritz Lipp committed
1047

1048
  /* adjust_view */
1049
  adjust_view(zathura);
1050 1051 1052 1053 1054 1055 1056 1057 1058
  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
1059 1060
    const double height = zathura_page_get_height(page);
    const double width = zathura_page_get_width(page);
1061 1062 1063 1064 1065 1066
    page_calc_height_width(zathura->document, height, width, &page_height, &page_width, true);
    gtk_widget_set_size_request(zathura->pages[page_id], page_width, page_height);

    /* show widget */
    gtk_widget_show(zathura->pages[page_id]);
  }
1067

1068
  /* Set page */
1069
  page_set(zathura, zathura_document_get_current_page_number(document));