pwmt / zathura

Document viewer
https://pwmt.org/projects/zathura
zlib License
2.1k stars 146 forks source link

How to send signals to `zathura` externally? #222

Open sebastinas opened 4 years ago

sebastinas commented 4 years ago

On GitLab by @jcguu95 on Aug 26, 2020, 19:25


Hoping for

I have a simple implementation of annotation (..etc) in mind, but I need a way to control zathura externally. For example, I hope the following scrolls the document

[me@laptop] $ zathura --instance=<ID> --command="sc_scroll"

where, sc_scroll is the built-in function that scrolls the document.

Question

How to send commands to zathura externally?


As a quick note, sc_scroll calls position_set at the end of itself. sc_scroll then calls refresh_view(zathura) in its end. Then refresh_view calls

  g_signal_emit(zathura->ui.session->gtk.view, zathura->signals.refresh_view,
                0, zathura);

which finally communicates with an external packages.

sebastinas commented 4 years ago

zathura is already capable of sending/receiving messages to/from other processs via dbus. The interface could be extended to receive more commands. Check out how this is currently done in zathura/dbus-interface.c.

sebastinas commented 4 years ago

On GitLab by @jcguu95 on Aug 31, 2020, 02:06


Thank you so much @sebastinas for reaching out! I'm not familiar with dbus but will take this chance to learn. Would you please provide a few simple usage examples? For example, how would I scroll down an opened document from terminal?

sebastinas commented 4 years ago

It's not that simple. You'd start at https://git.pwmt.org/pwmt/zathura/-/blob/develop/data/org.pwmt.zathura.xml and define a new method handling arbitrary commands. In https://git.pwmt.org/pwmt/zathura/-/blob/develop/zathura/dbus-interface.c, handle_method_call, one would then need to handle the new method and execute the command.

sebastinas commented 4 years ago

On GitLab by @jcguu95 on Aug 31, 2020, 14:16


@sebastinas thanks so much for your quick reply! I can see potentials here, and am so excited. I'm not familiar with dbus so this might be a dumb question:

This example aims to quickly involve people who don't know dbus to get started with it. In zathura.xml and dbus-interface.c it seems that a method GotoPage has been implemented

# in zathura.xml
<method name='GotoPage'>
  <arg type='u' name='page' direction='in' />
  <arg type='b' name='return' direction='out' />
</method>

# in dbus-interface.c
{ "GotoPage", handle_goto_page, true, true },

So, how can one launch this method from external terminal?

sebastinas commented 4 years ago
dbus-send --type="method_call" --dest=org.pwmt.zathura.PID-$pid /org/pwmt/zathura org.pwmt.zathura.GotoPage uint32:$page

where $pid is the PID of the process which should execute the action.

sebastinas commented 4 years ago

Alternatively you can also look at zathura's main.c and follow the synctex code path to see how this can be done with the dbus API. That code path also first searches for $pid based on the all running zathura process and their opened documents.

sebastinas commented 4 years ago

On GitLab by @jcguu95 on Aug 31, 2020, 22:00


Bad news.. just realized that zathura did not compile correctly for me.. so I'm not able to test the method I wrote. It could take a while before I start making contrib, if you feel worth it and keep helping I'd appreciate it!

Attempt to build dbus method ScrollDown

Just as an exercise, I tried to build a dbus method ScrollDown.

In dbus-interface.c I added

{ "ScrollDown", handle_scrolldown, true, true },

to handlers[], and added the function handle_scrolldown

static void
handle_scrolldown(zathura_t* zathura, GVariant* parameters,
                 GDBusMethodInvocation* invocation)
{
  const unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);

  // guint page = 0;
  // g_variant_get(parameters, "(u)", &page);

  guint session  = session; //  TODO ?
  guint argument = DOWN;
  guint event    = NULL; //     TODO ?
  guint t        = 1; // number of execution

  // g_variant_get(parameters, "u", &argument);

  bool ret = true;
  sc_scroll(session,argument,event,t); // TODO

  GVariant* result = g_variant_new("(b)", ret);
  g_dbus_method_invocation_return_value(invocation, result);
}

I'm not sure that the initial value of session and event is set right. I traced through the function sc_scroll. It seems that session is generated in girara already.. I don't know how to pass that session here while calling dbus. Similar situation holds for event.

(By the way, I did add the necessary in xml

    <!-- Scroll down. -->
    <method name='ScrollDown'>
      <arg type='u' name='argument' direction='in' />
      <arg type='b' name='return' direction='out' />
    </method>

Compilation error (unrelated to main topic thread)

I followed compilation guide

meson build
cd build
ninja

It generates a binary zathura in build, but an attempt to read file by it gives an error

./zathura book.pdf
error: could not open plugin directory: /usr/local/lib/zathura
error: Unable to initialize database. Bookmarks won't be available.
error: Could not determine file type.
sebastinas commented 4 years ago

Make sure to build with the same prefix or specify the plugin dir when running zathura (e.g., zathura -p /usr/lib/zathura).

sebastinas commented 4 years ago

On GitLab by @jcguu95 on Sep 1, 2020, 01:33


Ok, I solved the compilation problem. Now for the code, a new method ScrollDown is what I aim for:

  static const struct {
    const char* method;
    void (*handler)(zathura_t*, GVariant*, GDBusMethodInvocation*);
    bool needs_document;
    bool present_window;
  } handlers[] = {
    { "OpenDocument", handle_open_document, false, true },
    { "CloseDocument", handle_close_document, false, false },
    { "GotoPage", handle_goto_page, true, true },
    { "ScrollDown", handle_scrolldown, true, true },      // <----- this is new
    { "HighlightRects", handle_highlight_rects, true, true },
    { "SynctexView", handle_synctex_view, true, true }
  };

However, to do that I invoke the function sc_scroll(session,argument,event,t) in the body of the handlers[]: "ScrollDown"... The function sc_scroll calls input

girara_session_t* session,
girara_argument_t* argument,
girara_event_t* event

where session and event seem to be generated somewhere else by girara. It seems that fetching them externally requires another handler.. could you confirm this?

sebastinas commented 4 years ago

You can access the girara session via zathura->ui.session. argument needs to be populated depending on how you want to scroll. See sc_scroll for possible cases (and also check config.c). event is only used for bidrectional scrolling, so that only needs to be populated if you want to support that too.

sebastinas commented 4 years ago

On GitLab by @jcguu95 on Sep 1, 2020, 13:49


Yay.. after 6 hours I finally got it working. Here's the code

static void
handle_scroll(zathura_t* zathura, GVariant* parameters,
                 GDBusMethodInvocation* invocation)
{
  /*
  Usage:

  #+begin_src shell
    dbus-send --type="method_call" --print-reply \
    --dest=org.pwmt.zathura.PID-${pid} \
    /org/pwmt/zathura org.pwmt.zathura.Scroll int32:${n}
  #+end_src

  n = 2 ... LEFT
  n = 3 ... RIGHT
  n = 4 ... UP
  n = 5 ... DOWN
  n = 6 ... BOTTOM
  n = 7 ... TOP

  More to be found in the block `enum..` in `zathura.h`.
  */

  girara_session_t*  session  = zathura->ui.session ;
  girara_argument_t  argument                       ;
  girara_event_t*    event    = NULL                ;
  unsigned int       t        = 1                   ; // number of execution

  g_variant_get(parameters, "(i)", &(argument.n));
  argument.data = NULL;

  bool ret = true;
  sc_scroll(session,&argument,event,t);

  GVariant* result = g_variant_new("(b)", ret);
  g_dbus_method_invocation_return_value(invocation, result);
}

Do you find it useful (should I PR)? Or you want other methods to be implemented?

sebastinas commented 4 years ago

Yes, I think this would be useful in general. But it'd be interesting to handle more than just scroll.

sebastinas commented 4 years ago

On GitLab by @jcguu95 on Sep 4, 2020, 24:14


I think so! Do you have some interesting methods in mind?

I'm thinking of

  1. Jump to a specific location.
  2. Dump current state: such as position, mouse location, highlighted text, current document name, part of the document that's being viewed (as an image file).. etc.

Such functionalities will be enough for taking quick notes and annotations externally. If it's not too taxing for you, could you point out the difficulties of these, and perhaps some useful functions to look at :) Thank you!

sebastinas commented 2 years ago

Are you still interested in turning this into a MR?

sebastinas commented 1 year ago

On GitLab by @rmorales on Sep 21, 2023, 20:46


@jcguu95 @sebastinas In merge request !84, I've introduced a Dbus handler that can call any shortcut function. I believe this solution is more generic as it make it possible to call sc_scroll and all other shortcut functions. Please consider take a look at the merge request.