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

Moritz Lipp's avatar
Moritz Lipp committed
3
4
#include <stdlib.h>

5
6
#include <girara.h>

7
#include "bookmarks.h"
Moritz Lipp's avatar
Moritz Lipp committed
8
9
#include "callbacks.h"
#include "config.h"
10
#include "document.h"
Moritz Lipp's avatar
Moritz Lipp committed
11
#include "shortcuts.h"
Moritz Lipp's avatar
Moritz Lipp committed
12
#include "zathura.h"
Moritz Lipp's avatar
Moritz Lipp committed
13
#include "utils.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
14
#include "render.h"
Moritz Lipp's avatar
Moritz Lipp committed
15

Moritz Lipp's avatar
Update    
Moritz Lipp committed
16
17
18
19
20
21
22
23
typedef struct zathura_document_info_s
{
  zathura_t* zathura;
  const char* path;
  const char* password;
} zathura_document_info_t;

gboolean document_info_open(gpointer data);
Moritz Lipp's avatar
Moritz Lipp committed
24

Moritz Lipp's avatar
Moritz Lipp committed
25
/* function implementation */
Moritz Lipp's avatar
Moritz Lipp committed
26
27
zathura_t*
zathura_init(int argc, char* argv[])
28
{
Sebastian Ramacher's avatar
Sebastian Ramacher committed
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  /* parse command line options */
  GdkNativeWindow embed = 0;
  gchar* config_dir = NULL, *data_dir = NULL, *plugin_path = NULL;
  GOptionEntry entries[] = 
  {
    { "reparent",    'e', 0, G_OPTION_ARG_INT,      &embed,       "Reparents to window specified by xid",       "xid"  },
    { "config-dir",  'c', 0, G_OPTION_ARG_FILENAME, &config_dir,  "Path to the config directory",               "path" },
    { "data-dir",    'd', 0, G_OPTION_ARG_FILENAME, &data_dir,    "Path to the data directory",                 "path" },
    { "plugins-dir", 'p', 0, G_OPTION_ARG_STRING,   &plugin_path, "Path to the directories containing plugins", "path" },
    { NULL }
  };

  GOptionContext* context = g_option_context_new(" [file] [password]");
  g_option_context_add_main_entries(context, entries, NULL);

  GError* error = NULL;
  if (!g_option_context_parse(context, &argc, &argv, &error))
  {
    printf("Error parsing command line arguments: %s\n", error->message);
    g_option_context_free(context);
    g_error_free(error);
50
    goto error_free;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
51
52
53
  }
  g_option_context_free(context);

54
55
56
57
58
59
60
61
62
63
64
65
66
67
  zathura_t* zathura = malloc(sizeof(zathura_t));

  if (zathura == NULL) {
    return NULL;
  }

  /* general */
  zathura->document = NULL;

  /* plugins */
  zathura->plugins.plugins = girara_list_new();
  zathura->plugins.path    = girara_list_new();
  girara_list_set_free_function(zathura->plugins.path, g_free);

Sebastian Ramacher's avatar
Sebastian Ramacher committed
68
69
70
71
72
73
74
75
76
77
78
79
  if (config_dir) {
    zathura->config.config_dir = g_strdup(config_dir);
  } else {
    gchar* path = girara_get_xdg_path(XDG_CONFIG);
    zathura->config.config_dir = g_build_filename(path, "zathura", NULL);
    g_free(path);
  }

  if (data_dir) {
    zathura->config.data_dir = g_strdup(config_dir);
  } else {
    gchar* path = girara_get_xdg_path(XDG_DATA);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
80
    zathura->config.data_dir = g_build_filename(path, "zathura", NULL);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
    g_free(path);
  }

  if (plugin_path) {
    gchar** paths = g_strsplit(plugin_path, ":", 0);
    for (unsigned int i = 0; paths[i] != '\0'; ++i) {
      girara_list_append(zathura->plugins.path, g_strdup(paths[i]));
    }
    g_strfreev(paths);
  } else {
    /* XXX: this shouldn't be hard coded! */
    girara_list_append(zathura->plugins.path, g_strdup("/usr/local/lib/zathura"));
    girara_list_append(zathura->plugins.path, g_strdup("/usr/lib/zathura"));
  }

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

Moritz Lipp's avatar
Moritz Lipp committed
101
  zathura->ui.session->global.data  = zathura;
Moritz Lipp's avatar
Moritz Lipp committed
102
103
104
105
106
  zathura->ui.statusbar.file        = NULL;
  zathura->ui.statusbar.buffer      = NULL;
  zathura->ui.statusbar.page_number = NULL;
  zathura->ui.page_view             = NULL;
  zathura->ui.index                 = NULL;
107

Moritz Lipp's avatar
Moritz Lipp committed
108
  /* print settings */
109
110
111
  zathura->print.settings   = NULL;
  zathura->print.page_setup = NULL;

Moritz Lipp's avatar
Moritz Lipp committed
112
113
114
  /* global settings */
  zathura->global.recolor = false;

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  /* load plugins */
  zathura_document_plugins_load(zathura);

  /* configuration */
  config_load_default(zathura);

  /* load global configuration files */
  config_load_file(zathura, GLOBAL_RC);

  /* load local configuration files */
  char* configuration_file = g_build_filename(zathura->config.config_dir, ZATHURA_RC, NULL);
  config_load_file(zathura, configuration_file);
  free(configuration_file);

  /* initialize girara */
  zathura->ui.session->gtk.embed = embed;
  if (girara_session_init(zathura->ui.session) == false) {
    goto error_out;
  }

  /* girara events */
  zathura->ui.session->events.buffer_changed = buffer_changed;

138
  /* page view */
139
  zathura->ui.page_view = gtk_table_new(0, 0, TRUE);
Moritz Lipp's avatar
Moritz Lipp committed
140
  if (!zathura->ui.page_view) {
141
142
143
    goto error_free;
  }

Moritz Lipp's avatar
Moritz Lipp committed
144
145
146
147
148
149
  /* callbacks */
  GtkAdjustment* view_vadjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
  g_signal_connect(G_OBJECT(view_vadjustment), "value-changed", G_CALLBACK(cb_view_vadjustment_value_changed), zathura);
  GtkAdjustment* view_hadjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
  g_signal_connect(G_OBJECT(view_hadjustment), "value-changed", G_CALLBACK(cb_view_vadjustment_value_changed), zathura);

Moritz Lipp's avatar
Moritz Lipp committed
150
  gtk_widget_show(zathura->ui.page_view);
Moritz Lipp's avatar
Moritz Lipp committed
151

152
  /* statusbar */
Moritz Lipp's avatar
Moritz Lipp committed
153
154
  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
155
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
156
157
  }

Moritz Lipp's avatar
Moritz Lipp committed
158
159
  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
160
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
161
162
  }

Moritz Lipp's avatar
Moritz Lipp committed
163
164
  zathura->ui.statusbar.page_number = girara_statusbar_item_add(zathura->ui.session, FALSE, FALSE, FALSE, NULL);
  if (!zathura->ui.statusbar.page_number) {
Moritz Lipp's avatar
Moritz Lipp committed
165
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
166
167
  }

Moritz Lipp's avatar
Moritz Lipp committed
168
  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, "[No Name]");
Moritz Lipp's avatar
Moritz Lipp committed
169

Moritz Lipp's avatar
Moritz Lipp committed
170
  /* signals */
Moritz Lipp's avatar
Moritz Lipp committed
171
  g_signal_connect(G_OBJECT(zathura->ui.session->gtk.window), "destroy", G_CALLBACK(cb_destroy), NULL);
172

Moritz Lipp's avatar
Moritz Lipp committed
173
174
175
176
  /* save page padding */
  int* page_padding = girara_setting_get(zathura->ui.session, "page-padding");
  zathura->global.page_padding = (page_padding) ? *page_padding : 1;

Moritz Lipp's avatar
Moritz Lipp committed
177
178
179
  gtk_table_set_row_spacings(GTK_TABLE(zathura->ui.page_view), zathura->global.page_padding);
  gtk_table_set_col_spacings(GTK_TABLE(zathura->ui.page_view), zathura->global.page_padding);

Moritz Lipp's avatar
Moritz Lipp committed
180
181
182
183
184
185
186
187
188
189
190
191
192
  /* parse colors */
  char* string_value = girara_setting_get(zathura->ui.session, "recolor-dark-color");
  if (string_value != NULL) {
    gdk_color_parse(string_value, &(zathura->ui.colors.recolor_dark_color));
    free(string_value);
  }

  string_value = girara_setting_get(zathura->ui.session, "recolor-light-color");
  if (string_value != NULL) {
    gdk_color_parse(string_value, &(zathura->ui.colors.recolor_light_color));
    free(string_value);
  }

193
194
195
196
  /* bookmarks */
  zathura->bookmarks.bookmarks = girara_list_new();
  girara_list_set_free_function(zathura->bookmarks.bookmarks, (girara_free_function_t) zathura_bookmark_free);

Moritz Lipp's avatar
Moritz Lipp committed
197
  /* open document if passed */
Moritz Lipp's avatar
Update    
Moritz Lipp committed
198
199
200
201
202
203
204
205
206
207
208
  if (argc > 1) {
    zathura_document_info_t* document_info = malloc(sizeof(zathura_document_info_t));

    if (document_info != NULL) {
      document_info->zathura  = zathura;
      document_info->path     = argv[1];
      document_info->password = (argc >= 2) ? argv[2] : NULL;
      g_idle_add(document_info_open, document_info);
    }
  }

Moritz Lipp's avatar
Moritz Lipp committed
209
  return zathura;
Moritz Lipp's avatar
Moritz Lipp committed
210
211
212

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
213
214
  if (zathura->ui.page_view) {
    g_object_unref(zathura->ui.page_view);
215
216
  }

Moritz Lipp's avatar
Moritz Lipp committed
217
  girara_session_destroy(zathura->ui.session);
Moritz Lipp's avatar
Moritz Lipp committed
218
219
220

error_out:

Moritz Lipp's avatar
Moritz Lipp committed
221
222
  free(zathura);

Moritz Lipp's avatar
Moritz Lipp committed
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  return NULL;
}

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

  if (zathura->ui.session != NULL) {
    girara_session_destroy(zathura->ui.session);
  }

  document_close(zathura);

239
240
241
  /* bookmarks */
  girara_list_free(zathura->bookmarks.bookmarks);

242
243
244
245
  /* free print settings */
  g_object_unref(zathura->print.settings);
  g_object_unref(zathura->print.page_setup);

Moritz Lipp's avatar
Moritz Lipp committed
246
  /* free registered plugins */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
247
  zathura_document_plugins_free(zathura);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
248
  girara_list_free(zathura->plugins.plugins);
249
250
251
252
253
  girara_list_free(zathura->plugins.path);

  /* free config variables */
  g_free(zathura->config.config_dir);
  g_free(zathura->config.data_dir);
254
255
}

Moritz Lipp's avatar
Update    
Moritz Lipp committed
256
257
258
259
260
261
262
263
264
265
266
267
gboolean
document_info_open(gpointer data)
{
  zathura_document_info_t* document_info = data;
  g_return_val_if_fail(document_info != NULL, FALSE);

  if (document_info->zathura == NULL || document_info->path == NULL) {
    free(document_info);
    return FALSE;
  }

  document_open(document_info->zathura, document_info->path, document_info->password);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
268
  free(document_info);
Moritz Lipp's avatar
Update    
Moritz Lipp committed
269
270

  return FALSE;
271
272
}

Moritz Lipp's avatar
Moritz Lipp committed
273
bool
Moritz Lipp's avatar
Moritz Lipp committed
274
document_open(zathura_t* zathura, const char* path, const char* password)
Moritz Lipp's avatar
Moritz Lipp committed
275
{
Moritz Lipp's avatar
Moritz Lipp committed
276
  if (!path) {
277
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
278
279
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
280
  zathura_document_t* document = zathura_document_open(zathura, path, password);
Moritz Lipp's avatar
Moritz Lipp committed
281

Moritz Lipp's avatar
Moritz Lipp committed
282
  if (!document) {
283
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
284
285
  }

Moritz Lipp's avatar
Moritz Lipp committed
286
  zathura->document = document;
Moritz Lipp's avatar
Moritz Lipp committed
287

288
  /* view mode */
Moritz Lipp's avatar
Moritz Lipp committed
289
  int* value = girara_setting_get(zathura->ui.session, "pages-per-row");
290
291
  int pages_per_row = (value) ? *value : 1;
  free(value);
Moritz Lipp's avatar
Moritz Lipp committed
292
  page_view_set_mode(zathura, pages_per_row);
Moritz Lipp's avatar
Moritz Lipp committed
293

Moritz Lipp's avatar
Moritz Lipp committed
294
  girara_set_view(zathura->ui.session, zathura->ui.page_view);
Moritz Lipp's avatar
Moritz Lipp committed
295

Moritz Lipp's avatar
Moritz Lipp committed
296
  /* threads */
Moritz Lipp's avatar
Moritz Lipp committed
297
  zathura->sync.render_thread = render_init(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
298

Moritz Lipp's avatar
Moritz Lipp committed
299
  if (!zathura->sync.render_thread) {
Moritz Lipp's avatar
Moritz Lipp committed
300
301
302
    goto error_free;
  }

Moritz Lipp's avatar
Moritz Lipp committed
303
304
305
306
  /* create blank pages */
  for (int page_id = 0; page_id < document->number_of_pages; page_id++) {
    zathura_page_t* page = document->pages[page_id];
    gtk_widget_realize(page->event_box);
307
  }
Moritz Lipp's avatar
Moritz Lipp committed
308
309

  return true;
310
311
312
313
314
315
316
317

error_free:

  zathura_document_free(document);

error_out:

  return false;
Moritz Lipp's avatar
Moritz Lipp committed
318
319
}

320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
bool
document_save(zathura_t* zathura, const char* path, bool overwrite)
{
  g_return_val_if_fail(zathura, false);
  g_return_val_if_fail(zathura->document, false);
  g_return_val_if_fail(path, false);

  gchar* file_path = girara_fix_path(path);
  if (!overwrite && g_file_test(file_path, G_FILE_TEST_EXISTS))
  {
    gchar* message = g_strdup_printf("File already exists: %s. Use :write! to overwrite it.", file_path);
    girara_error(message);
    g_free(message);
    return false;
  }

  bool res = zathura_document_save_as(zathura->document, file_path);
  g_free(file_path);
  return res;
}

Pavel Borzenkov's avatar
Pavel Borzenkov committed
341
342
343
344
345
346
347
348
349
350
static void
remove_page_from_table(GtkWidget* page, gpointer permanent)
{
  if (!permanent) {
    g_object_ref(G_OBJECT(page));
  }

  gtk_container_remove(GTK_CONTAINER(page->parent), page);
}

Moritz Lipp's avatar
Moritz Lipp committed
351
bool
Moritz Lipp's avatar
Moritz Lipp committed
352
document_close(zathura_t* zathura)
Moritz Lipp's avatar
Moritz Lipp committed
353
{
Moritz Lipp's avatar
Moritz Lipp committed
354
  if (!zathura->document) {
Moritz Lipp's avatar
Moritz Lipp committed
355
356
357
    return false;
  }

Pavel Borzenkov's avatar
Pavel Borzenkov committed
358
359
360
361
  render_free(zathura->sync.render_thread);
  zathura->sync.render_thread = NULL;

  gtk_container_foreach(GTK_CONTAINER(zathura->ui.page_view), remove_page_from_table, (gpointer)1);
Moritz Lipp's avatar
Moritz Lipp committed
362

Moritz Lipp's avatar
Moritz Lipp committed
363
  zathura_document_free(zathura->document);
Pavel Borzenkov's avatar
Pavel Borzenkov committed
364
365
366
  zathura->document = NULL;

  gtk_widget_hide_all(zathura->ui.page_view);
Moritz Lipp's avatar
Moritz Lipp committed
367
368
369
370
371

  return true;
}

bool
Moritz Lipp's avatar
Moritz Lipp committed
372
page_set(zathura_t* zathura, unsigned int page_id)
Moritz Lipp's avatar
Moritz Lipp committed
373
{
Moritz Lipp's avatar
Moritz Lipp committed
374
  if (!zathura->document || !zathura->document->pages) {
Moritz Lipp's avatar
Moritz Lipp committed
375
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
376
377
  }

Moritz Lipp's avatar
Moritz Lipp committed
378
  if (page_id >= zathura->document->number_of_pages) {
Moritz Lipp's avatar
Moritz Lipp committed
379
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
380
381
  }

382
  /* render page */
Moritz Lipp's avatar
Moritz Lipp committed
383
  zathura_page_t* page = zathura->document->pages[page_id];
Moritz Lipp's avatar
Moritz Lipp committed
384

Moritz Lipp's avatar
Moritz Lipp committed
385
  if (!page) {
386
387
388
    goto error_out;
  }

Moritz Lipp's avatar
Moritz Lipp committed
389
390
391
392
393
  page_offset_t* offset = page_calculate_offset(page);
  if (offset == NULL) {
    goto error_out;
  }

Moritz Lipp's avatar
Moritz Lipp committed
394
  GtkAdjustment* view_vadjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
Moritz Lipp's avatar
Moritz Lipp committed
395
396
397
  GtkAdjustment* view_hadjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
  gtk_adjustment_set_value(view_hadjustment, offset->x);
  gtk_adjustment_set_value(view_vadjustment, offset->y);
398
399

  /* update page number */
Moritz Lipp's avatar
Moritz Lipp committed
400
  zathura->document->current_page_number = page_id;
401
  statusbar_page_number_update(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
402
403

  return true;
Moritz Lipp's avatar
Moritz Lipp committed
404
405
406
407

error_out:

  return false;
Moritz Lipp's avatar
Moritz Lipp committed
408
409
}

410
411
412
413
414
415
416
417
418
419
420
421
void
statusbar_page_number_update(zathura_t* zathura)
{
  if (zathura == NULL || zathura->ui.statusbar.page_number == NULL) {
    return;
  }

  char* page_number_text = g_strdup_printf("[%d/%d]", zathura->document->current_page_number + 1, zathura->document->number_of_pages);
  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.page_number, page_number_text);
  g_free(page_number_text);
}

422
void
Moritz Lipp's avatar
Moritz Lipp committed
423
page_view_set_mode(zathura_t* zathura, unsigned int pages_per_row)
424
{
Moritz Lipp's avatar
Moritz Lipp committed
425
426
427
428
429
  /* show at least one page */
  if (pages_per_row == 0) {
    pages_per_row = 1;
  }

430
431
432
433
  if (zathura->document == NULL) {
    return;
  }

Pavel Borzenkov's avatar
Pavel Borzenkov committed
434
  gtk_container_foreach(GTK_CONTAINER(zathura->ui.page_view), remove_page_from_table, (gpointer)0);
435

436
  gtk_table_resize(GTK_TABLE(zathura->ui.page_view), zathura->document->number_of_pages / pages_per_row + 1, pages_per_row);
Moritz Lipp's avatar
Moritz Lipp committed
437
  for (unsigned int i = 0; i < zathura->document->number_of_pages; i++)
438
  {
439
440
    int x = i % pages_per_row;
    int y = i / pages_per_row;
Moritz Lipp's avatar
Moritz Lipp committed
441
    gtk_table_attach(GTK_TABLE(zathura->ui.page_view), zathura->document->pages[i]->event_box, x, x + 1, y, y + 1, GTK_EXPAND, GTK_EXPAND, 0, 0);
442
443
  }

Moritz Lipp's avatar
Moritz Lipp committed
444
  gtk_widget_show_all(zathura->ui.page_view);
445
446
}

Moritz Lipp's avatar
Moritz Lipp committed
447
/* main function */
448
int main(int argc, char* argv[])
Moritz Lipp's avatar
Moritz Lipp committed
449
{
450
451
  g_thread_init(NULL);
  gdk_threads_init();
Moritz Lipp's avatar
Moritz Lipp committed
452
453
  gtk_init(&argc, &argv);

Moritz Lipp's avatar
Moritz Lipp committed
454
455
  zathura_t* zathura = zathura_init(argc, argv);
  if (zathura == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
456
457
458
    printf("error: coult not initialize zathura\n");
    return -1;
  }
Moritz Lipp's avatar
Moritz Lipp committed
459

Moritz Lipp's avatar
Moritz Lipp committed
460
461
462
  gdk_threads_enter();
  gtk_main();
  gdk_threads_leave();
463

Moritz Lipp's avatar
Moritz Lipp committed
464
465
  return 0;
}