Connection between XFCE API signals and Plug in Python/Javascript – Part 2

0
6 min read

It was a major challenge to receive socket emitted signal in the plug inside a socket program written in C programming language. So we decided to move forward with dbus approach as discussed in last blog.

Inside a plugin we created dbus server part in gtkplug written in python and dbus client part in gtksocket. Benefit of this approach was we can transmit signals emitted by gtk plug to gtksocket part through dbus. As plug is embedded inside the gtksocket, so most of the events will be happening throughout the gtkplug leading to emission of different signals on different occasion. Yes, there is an way to access different XFCE API functions through the python. But we can’t register XFCE plugin through the python, hence sample plugin object was registered before creation of Gtksocket through C programming.

This challenge was the biggest challenge that why things became complex even for performing simple things.

so let me show you an example for client dbus in C and server dbus in python.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> 
#include <string.h>
#include <stdbool.h>
#include <ctype.h>

#include <dbus/dbus.h>


const char *const INTERFACE_NAME = "com.example.service.Print";
const char *const SERVER_BUS_NAME = "com.example.service";
const char *const CLIENT_BUS_NAME = "com.example.clientservice";
const char *const SERVER_OBJECT_PATH_NAME = "/com/example/service";
const char *const CLIENT_OBJECT_PATH_NAME = "/com/example/clientservice";
const char *const METHOD_NAME = "print";

const char *const ANOTHER_INTERFACE_NAME = "com.example.service.Quit";
const char *const ANOTHER_METHOD_NAME = "quit";

DBusError dbus_error;
void print_dbus_error (char *str);


int sendMethodCall(void) {
    /*
    This function is created to call quit method for server Dbus
    After sucessfull call through this function server dbus will get stopped.
    */
    DBusMessage *message;
    DBusPendingCall *pending;

    DBusConnection *conn;
    
    dbus_error_init (&dbus_error);

    conn = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error);

    message = dbus_message_new_method_call(SERVER_BUS_NAME, SERVER_OBJECT_PATH_NAME, 
                           ANOTHER_INTERFACE_NAME, ANOTHER_METHOD_NAME);
    if (message == NULL) {
        fprintf(stderr, "Message is NULL \n");
        return -1;
    }

    if (!dbus_connection_send_with_reply(conn, message, &pending, -1)) {
        fprintf(stderr, "Out of memory \n");
        return -1;
    }

    if (pending == NULL) {
        fprintf(stderr, "Pending call is NULL \n");
        return -1;
    }
    printf("Method call has been sent.\n");

    dbus_message_unref(message);
    dbus_pending_call_block(pending);

    message = dbus_pending_call_steal_reply(pending);
    if (message == NULL) {
        fprintf(stderr, "Reply message is NULL \n");
        return -1;
    }

    printf("Received reply.\n");

    dbus_pending_call_unref(pending);
    dbus_message_unref(message);
}

int main (int argc, char **argv)
{
    DBusConnection *conn;
    int ret;
    char input [80];

    dbus_error_init (&dbus_error);

    conn = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error);

    if (dbus_error_is_set (&dbus_error))
        print_dbus_error ("dbus_bus_get");

    if (!conn) 
        exit (1);

    printf ("Please type two numbers: ");
    while (fgets (input, 78, stdin) != NULL) {

        // Get a well known name
        while (1) {
            ret = dbus_bus_request_name (conn, CLIENT_BUS_NAME, 0, &dbus_error);

            if (ret == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) 
               break;

            if (ret == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
               fprintf (stderr, "Waiting for the bus ... \n");
               sleep (1);
               continue;
            }
            if (dbus_error_is_set (&dbus_error))
               print_dbus_error ("dbus_bus_get");
        }
        
        DBusMessage *request;

        
        

        if ((request = dbus_message_new_method_call (SERVER_BUS_NAME, SERVER_OBJECT_PATH_NAME, 
                           INTERFACE_NAME, METHOD_NAME)) == NULL) {
            fprintf (stderr, "Error in dbus_message_new_method_call\n");
            exit (1);
        }

        DBusMessageIter iter;

        // method to append args into message dbus
        dbus_message_iter_init_append (request, &iter);

        char *ptr = input;
        if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &ptr)) {
            fprintf (stderr, "Error in dbus_message_iter_append_basic\n");
            exit (1);
        }
        DBusPendingCall *pending_return;
        if (!dbus_connection_send_with_reply (conn, request, &pending_return, -1)) {
            fprintf (stderr, "Error in dbus_connection_send_with_reply\n");
            exit (1);
        }

        if (pending_return == NULL) {
            fprintf (stderr, "pending return is NULL");
            exit (1);
        }

        dbus_connection_flush (conn);
                
        //free message
        dbus_message_unref (request);	

        dbus_pending_call_block (pending_return);

        DBusMessage *reply;
        if ((reply = dbus_pending_call_steal_reply (pending_return)) == NULL) {
            fprintf (stderr, "Error in dbus_pending_call_steal_reply");
            exit (1);
        }

        dbus_pending_call_unref	(pending_return);

        char *s;
        if (dbus_message_get_args (reply, &dbus_error, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID)) {
            printf ("%s\n", s);
            // Send method call
            printf("Send d-bus method call\n");
            if (sendMethodCall() != 0) {
                return -1;
            }

        }
        else
        {
             fprintf (stderr, "Did not get arguments in reply\n");
             exit (1);
        }
        dbus_message_unref (reply);	

        if (dbus_bus_release_name (conn, CLIENT_BUS_NAME, &dbus_error) == -1) {
             fprintf (stderr, "Error in dbus_bus_release_name\n");
             exit (1);
        }

        printf ("Please type two numbers: ");
    }

    return 0;
}

void print_dbus_error (char *str) 
{
    fprintf (stderr, "%s: %s\n", str, dbus_error.message);
    dbus_error_free (&dbus_error);
}

Above program takes input as a number and then pass it to server dbus in python, where you can perform any operation related to input number and then pass result through dbus back to client side in C. After printing out received result in client side, it print out the received message and make a call to sendMethodCall() which makes a call to quit function of server dus in python and leading to stoppage in operations of server dbus service.

This program can be optimise through struct , above code contain lot of repetitive things 🙁

Here is an code snippet of server dbus in python.

#!/usr/bin/env python3

import dbus
import dbus.service
import dbus.mainloop.glib

from gi.repository import GLib


class Service(dbus.service.Object):
   """
   Server dbus example in Python
   """

   def run(self):
      dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
      
      # define service bus name
      bus_name = dbus.service.BusName("com.example.service", dbus.SessionBus())
      dbus.service.Object.__init__(self, bus_name, "/com/example/service")

      self._loop = GLib.MainLoop()
      print("Service running...")

      self._loop.run()
      print("Service stopped")

   @dbus.service.method("com.example.service.Message", in_signature='', out_signature='s')
   def get_message(self):
      """
      Function without input string but string as output

      Typecast of output value is defined as s as value of out_signature
      """
      print("  sending message")
      return self._message

   @dbus.service.method("com.example.service.Quit", in_signature='', out_signature='')
   def quit(self):
      """
      function to quit mainloop
      """
      print("  shutting down")
      self._loop.quit()

   @dbus.service.method("com.example.service.Print", in_signature='i', out_signature='s')
   def print(self, input_string):
      """
      Function with input parameter

      Typecast of input value is defined as s as value of in_signature
      Typecast of output value is defined as s as value of out_signature

      """

      print("  print method", input_string)
      return "received two input number string" + input_string


Service().run()

above code snippet have been explained in last blog, you can take reference of last blog through url, https://freshlybuilt.com/connection-between-xfce-api-signals-and-plug-in-python-javascript-part-1/

Output – Server side

Output – Client side

Hope so you would have better idea, why we have used this approach, and through these code snippet I have tried to give you an idea, how does this approach work.

All type of suggestions are welcome in comment section of blog 🙂

Choose your Reaction!
Leave a Comment