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

3
#include <glib.h>
4
#include <girara/utils.h>
5

6 7 8 9
#include "synctex.h"
#include "zathura.h"
#include "page.h"
#include "document.h"
10
#include "utils.h"
11

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
enum {
  SYNCTEX_RESULT_BEGIN = 1,
  SYNCTEX_RESULT_END,
  SYNCTEX_PROP_PAGE,
  SYNCTEX_PROP_H,
  SYNCTEX_PROP_V,
  SYNCTEX_PROP_WIDTH,
  SYNCTEX_PROP_HEIGHT,
};

typedef struct token_s {
  const char* name;
  guint token;
} token_t;

static token_t scanner_tokens[] = {
  {"SyncTeX result begin", SYNCTEX_RESULT_BEGIN},
  {"SyncTeX result end", SYNCTEX_RESULT_END},
  {"Page:", SYNCTEX_PROP_PAGE},
  {"h:", SYNCTEX_PROP_H},
  {"v:", SYNCTEX_PROP_V},
  {"W:", SYNCTEX_PROP_WIDTH},
  {"H:", SYNCTEX_PROP_HEIGHT},
  {NULL, 0}
};

static GScannerConfig scanner_config = {
  .cset_skip_characters  = "\n\r",
  .cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z,
  .cset_identifier_nth   = G_CSET_a_2_z G_CSET_A_2_Z ": ",
  .cpair_comment_single  = NULL,
  .case_sensitive        = TRUE,
  .scan_identifier       = TRUE,
  .scan_symbols          = TRUE,
  .scan_float            = TRUE,
  .numbers_2_int         = TRUE,
};

50 51 52
void
synctex_edit(zathura_t* zathura, zathura_page_t* page, int x, int y)
{
Moritz Lipp's avatar
Moritz Lipp committed
53 54 55
  if (zathura == NULL || page == NULL) {
    return;
  }
56

Moritz Lipp's avatar
Moritz Lipp committed
57 58
  zathura_document_t* document = zathura_page_get_document(page);
  if (document == NULL) {
59
    return;
Moritz Lipp's avatar
Moritz Lipp committed
60 61 62 63 64 65 66
  }

  const char *filename = zathura_document_get_path(document);
  if (filename == NULL) {
    return;
  }

67
  char** argv = g_try_malloc0(sizeof(char*) * (zathura->synctex.editor != NULL ?
68
      7 : 5));
69 70 71 72
  if (argv == NULL) {
    return;
  }

73 74 75
  argv[0] = g_strdup("synctex");
  argv[1] = g_strdup("edit");
  argv[2] = g_strdup("-o");
76 77
  argv[3] = g_strdup_printf("%d:%d:%d:%s", zathura_page_get_index(page) + 1, x,
      y, filename);
Moritz Lipp's avatar
Moritz Lipp committed
78
  if (zathura->synctex.editor != NULL) {
79 80
    argv[4] = g_strdup("-x");
    argv[5] = g_strdup(zathura->synctex.editor);
81 82
  }

83 84
  g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
  g_strfreev(argv);
85
}
86 87

static double
Moritz Lipp's avatar
Moritz Lipp committed
88 89
scan_float(GScanner* scanner)
{
90 91 92 93 94 95 96 97 98 99
  switch (g_scanner_get_next_token(scanner)) {
    case G_TOKEN_FLOAT:
      return g_scanner_cur_value(scanner).v_float;
    case G_TOKEN_INT:
      return g_scanner_cur_value(scanner).v_int;
    default:
      return 0.0;
  }
}

100
girara_list_t*
101
synctex_rectangles_from_position(const char* filename, const char* position,
102 103
                                 unsigned int* page,
                                 girara_list_t** secondary_rects)
104
{
105
  if (filename == NULL || position == NULL || page == NULL) {
106
    return NULL;
107 108
  }

109
  char** argv = g_try_malloc0(sizeof(char*) * 7);
110 111 112 113
  if (argv == NULL) {
    return NULL;
  }

114 115 116 117 118
  argv[0] = g_strdup("synctex");
  argv[1] = g_strdup("view");
  argv[2] = g_strdup("-i");
  argv[3] = g_strdup(position);
  argv[4] = g_strdup("-o");
119
  argv[5] = g_strdup(filename);
120

121
  gint output = -1;
122 123 124 125
  bool ret = g_spawn_async_with_pipes(NULL, argv, NULL,
      G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, NULL, NULL,
      &output, NULL, NULL);
  g_strfreev(argv);
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160

  if (ret == false) {
    return false;
  }

  GScanner* scanner = g_scanner_new(&scanner_config);
  token_t* tokens = scanner_tokens;
  while (tokens->name != NULL) {
    g_scanner_add_symbol(scanner, tokens->name, GINT_TO_POINTER(tokens->token));
    tokens++;
  }

  g_scanner_input_file(scanner, output);

  bool found_begin = false, found_end = false;
  while (found_begin == false && found_end == false) {
    switch (g_scanner_get_next_token(scanner)) {
      case G_TOKEN_EOF:
        found_end = true;
        break;

      case G_TOKEN_SYMBOL:
        switch (GPOINTER_TO_INT(g_scanner_cur_value(scanner).v_identifier)) {
          case SYNCTEX_RESULT_BEGIN:
            found_begin = true;
            break;
        }
        break;

      default:
        /* skip everything else */
        break;
    }
  }

161 162 163 164
  ret                        = false;
  unsigned int rpage         = 0;
  unsigned int current_page  = 0;
  girara_list_t* hitlist     = girara_list_new2(g_free);
165
  girara_list_t* other_rects = girara_list_new2(g_free);
166 167
  bool got_rect              = false;
  zathura_rectangle_t rectangle;
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182

  while (found_end == false) {
    switch (g_scanner_get_next_token(scanner)) {
      case G_TOKEN_EOF:
        found_end = true;
        break;

      case G_TOKEN_SYMBOL:
        switch (GPOINTER_TO_INT(g_scanner_cur_value(scanner).v_identifier)) {
          case SYNCTEX_RESULT_END:
            found_end = true;
            break;

          case SYNCTEX_PROP_PAGE:
            if (g_scanner_get_next_token(scanner) == G_TOKEN_INT) {
183
              current_page = g_scanner_cur_value(scanner).v_int - 1;
184 185
              if (ret == false) {
                ret = true;
186
                rpage = current_page;
187
              }
188

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
              if (got_rect == false) {
                continue;
              }
              got_rect = false;

              if (*page == current_page) {
                zathura_rectangle_t* real_rect = g_try_malloc(sizeof(zathura_rectangle_t));
                if (real_rect == NULL) {
                  continue;
                }

                *real_rect = rectangle;
                girara_list_append(hitlist, real_rect);
              } else {
                synctex_page_rect_t* page_rect = g_try_malloc(sizeof(synctex_page_rect_t));
204 205 206 207
                if (page_rect == NULL) {
                  continue;
                }

208
                page_rect->page = current_page;
209
                page_rect->rect = rectangle;
210

211
                girara_list_append(other_rects, page_rect);
212
              }
213 214 215 216
            }
            break;

          case SYNCTEX_PROP_H:
217 218
            rectangle.x1 = scan_float(scanner);
            got_rect     = true;
219 220
            break;

221 222 223
            case SYNCTEX_PROP_V:
            rectangle.y2 = scan_float(scanner);
            got_rect     = true;
224 225
            break;

226 227 228
            case SYNCTEX_PROP_WIDTH:
            rectangle.x2 = rectangle.x1 + scan_float(scanner);
            got_rect     = true;
229 230
            break;

231 232 233
            case SYNCTEX_PROP_HEIGHT:
            rectangle.y1 = rectangle.y2 - scan_float(scanner);
            got_rect     = true;
234 235 236 237 238 239 240 241 242
            break;
        }
        break;

      default:
        break;
    }
  }

243
  if (got_rect == true) {
244
    if (current_page == rpage) {
245 246 247 248 249
      zathura_rectangle_t* real_rect = g_try_malloc(sizeof(zathura_rectangle_t));
      if (real_rect != NULL) {
        *real_rect = rectangle;
        girara_list_append(hitlist, real_rect);
      }
250
    } else {
251
      synctex_page_rect_t* page_rect = g_try_malloc(sizeof(synctex_page_rect_t));
252 253
      if (page_rect != NULL) {
        page_rect->page = current_page;
254
        page_rect->rect = rectangle;
255 256
        girara_list_append(other_rects, page_rect);
      }
257
    }
258 259 260 261 262
  }

  g_scanner_destroy(scanner);
  close(output);

263 264 265 266 267 268 269
  if (ret == false) {
    girara_debug("Got no usable output from running synctex view.");
    girara_list_free(hitlist);
    girara_list_free(other_rects);
    return NULL;
  }

270 271 272 273 274 275 276 277 278
  if (page != NULL) {
    *page = rpage;
  }
  if (secondary_rects != NULL) {
    *secondary_rects = other_rects;
  } else {
    girara_list_free(other_rects);
  }

279
  return hitlist;
280
}