how to add Xorg event handing in g_main_loop - c

I have a lightweight application that catches Xorg and dbus events. In order to do this I initialized dbus loop and started g_main_loop, but I don't know how to add Xorg event handling in a natural way:
GMainLoop * mainloop = NULL;
mainloop = g_main_loop_new(NULL,FALSE);
dbus_g_thread_init ();
dbus_init();
// <<<<<<<<<<<<<<<<<<<<<<<<<
//1 way using timeout
//g_timeout_add(100, kbdd_default_iter, mainloop);
//2nd way using pthread
//GThread * t = g_thread_create(kbdd_default_loop, NULL, FALSE, NULL);
//>>>>>>>>>>>>>>>>>>>>>>>>>>>
g_main_loop_run(mainloop);
in default iter I'm checking if there is waiting X-event and handle them.
Both ways seems bad, first because I have unneeded calls for checking event, second because I make an additional thread and have to make additional locks.
P.S. I know I can use gtk lib, but I don't want to have dependencies on any toolkit.

If you want to add Xorg event handling to the main loop without using a timeout (which as you state is wasteful), you'll need to add a source that polls the X connection. For that, you'll need to get below the Xlib abstraction layer to get the underlying X connection file descriptor. That's what the complete program below does. It is an adaptation of C. Tronche's excellent X11 tutorial to use the glib main loop for polling. I also drew from "Foundations of GTK+ Development" by Andrew Krause.
If this doesn't seem very "natural", that's because I doubt there is a very "natural" way to do this - you're really re-implementing a core part of GDK here.
/* needed to break into 'Display' struct internals. */
#define XLIB_ILLEGAL_ACCESS
#include <X11/Xlib.h> // Every Xlib program must include this
#include <assert.h> // I include this to test return values the lazy way
#include <glib.h>
typedef struct _x11_source {
GSource source;
Display *dpy;
Window w;
} x11_source_t;
static gboolean
x11_fd_prepare(GSource *source,
gint *timeout)
{
*timeout = -1;
return FALSE;
}
static gboolean
x11_fd_check (GSource *source)
{
return TRUE;
}
static gboolean
x11_fd_dispatch(GSource* source, GSourceFunc callback, gpointer user_data)
{
static gint counter = 0;
Display *dpy = ((x11_source_t*)source)->dpy;
Window window = ((x11_source_t*)source)->w;
XEvent e;
while (XCheckWindowEvent(dpy,
window,
EnterWindowMask,
&e))
{
if (e.type == EnterNotify)
g_print("We're in!!! (%d)\n", ++counter);
}
return TRUE;
}
static gboolean
msg_beacon(gpointer data)
{
static gint counter = 0;
g_print("Beacon %d\n", ++counter);
return TRUE;
}
int
main()
{
Display *dpy = XOpenDisplay(NULL);
assert(dpy);
int blackColor = BlackPixel(dpy, DefaultScreen(dpy));
int whiteColor = WhitePixel(dpy, DefaultScreen(dpy));
Window w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0,
200, 100, 0, blackColor, blackColor);
XSelectInput(dpy, w, StructureNotifyMask | EnterWindowMask);
XMapWindow(dpy, w);
for (;;) {
XEvent e;
XNextEvent(dpy, &e);
if (e.type == MapNotify)
break;
}
GMainLoop *mainloop = NULL;
mainloop = g_main_loop_new(NULL, FALSE);
/* beacon to demonstrate we're not blocked. */
g_timeout_add(300, msg_beacon, mainloop);
GPollFD dpy_pollfd = {dpy->fd,
G_IO_IN | G_IO_HUP | G_IO_ERR,
0};
GSourceFuncs x11_source_funcs = {
x11_fd_prepare,
x11_fd_check,
x11_fd_dispatch,
NULL, /* finalize */
NULL, /* closure_callback */
NULL /* closure_marshal */
};
GSource *x11_source =
g_source_new(&x11_source_funcs, sizeof(x11_source_t));
((x11_source_t*)x11_source)->dpy = dpy;
((x11_source_t*)x11_source)->w = w;
g_source_add_poll(x11_source, &dpy_pollfd);
g_source_attach(x11_source, NULL);
g_main_loop_run(mainloop);
return 0;
}

Related

How do I pass a widget as client_data to a Xt/Motif signal handler?

I want a Motif application to redraw a Drawing Area widget on reception of SIGUSR1 signal.
I have configured the signal using the Xt functionalities in X11R6:
/* CONFIGURE READ SIGNAL, TRAPS SIGUSR1 TO FORCE APPLICATION READ DATA FILE */
signal(SIGUSR1, signal_usr1_handler);
signal_id = XtAppAddSignal (app, read_data, (XtPointer)chart_area);
Where signal_usr1_handler is the OS Unix handler and read_data is the Xt signal handler.
For the Xt signal handler I am trying to pass the widget chart_area which is a Drawing Area Widget.
These are the handlers:
/* SIGNAL HANDLER */
void signal_usr1_handler() {
printf("SIGUSR1 RECEIVED\n");
XtNoticeSignal(signal_id);
}
/* READ DATA XT SIGNAL HANDLER */
void read_data(XtPointer client_data, XtSignalId *id) {
posx += 5;
printf("XT HANDLES SIGUSR1\n");
XmDrawingAreaCallbackStruct da_struct;
da_struct.reason = XmCR_EXPOSE;
da_struct.event = (XEvent *) NULL;
da_struct.window = XtWindow((Widget)client_data);
XtCallCallbacks((Widget)client_data, XmNexposeCallback, (XtPointer) &da_struct);
}
The application raises a Segmentation fault (core dumped) exactly when executing the XtCallCallbacks function.
This is the complete source code:
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include <Xm/Xm.h>
#include <Xm/DrawingA.h>
#include <Xm/Label.h>
/* FUNCTION DECLARATIONS */
void draw_chart(Widget widget, XtPointer client_data, XtPointer call_data);
void signal_usr1_handler();
void read_data(XtPointer client_data, XtSignalId *id);
/* XT SIGNAL ID */
XtSignalId signal_id; /* Signal ID used to receive data read via SIGUSR1 */
int main(int argc, char *argv[]) {
/* WIDGETS */
XtAppContext app; /* Application Context */
Widget toplevel; /* Top Level Button */
Widget chart_area; /* Drawing Area Widget to draw the chart */
/* DRAWING AREA RELATED */
XGCValues gcv;
GC gc;
/* RESOURCE VALUE ARRAYS/COUNT */
Arg al[10];
int ac;
/* INITIALIZE TOP LEVEL WINDOW */
XtSetLanguageProc(NULL, NULL, NULL);
toplevel = XtVaOpenApplication(
&app, argv[0], NULL, 0, &argc, argv, NULL, sessionShellWidgetClass,
XmNwidth, 400, XmNheight, 300, NULL
);
/* CREATE AND MANAGE DRAWING CANVAS WIDGET */
ac=0;
chart_area = XmCreateDrawingArea(toplevel, "chart_area", al, ac);
/* CREATE GRAPHICS CONTEXT */
gcv.foreground = WhitePixelOfScreen(XtScreen(chart_area));
gcv.background = BlackPixelOfScreen(XtScreen(chart_area));
gc = XCreateGC (
XtDisplay(chart_area),
RootWindowOfScreen(XtScreen(chart_area)),
(GCForeground | GCBackground),
&gcv);
/* ASSIGN GRAPHICS CONTEXT */
XtVaSetValues(chart_area, XmNuserData, gc, NULL);
/* ASSIGN CALLBACKS AND MANAGE WIDGET */
XtAddCallback(chart_area, XmNexposeCallback, draw_chart, NULL);
XtManageChild(chart_area);
/* CONFIGURE READ SIGNAL, TRAPS SIGUSR1 TO FORCE APPLICATION READ DATA FILE */
signal(SIGUSR1, signal_usr1_handler);
signal_id = XtAppAddSignal (app, read_data, (XtPointer)chart_area);
/* REALIZE TOPLEVEL WINDOW AND LAUNCH APPLICATION LOOP */
XtRealizeWidget(toplevel);
XtAppMainLoop(app);
return 0;
}
void draw_chart(Widget widget, XtPointer client_data, XtPointer call_data) {
XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct *) call_data;
XEvent *event = cbs->event;
Display *dpy = event->xany.display;
GC gc;
/* DRAW LINE */
if(cbs->reason == XmCR_EXPOSE || cbs->reason == XmCR_ACTIVATE) {
XtVaGetValues(widget, XmNuserData, &gc, NULL);
XDrawLine(dpy, cbs->window, gc, 10, 10, posx, 200);
}
}
/* SIGNAL HANDLER */
void signal_usr1_handler() {
printf("SIGUSR1 RECEIVED\n");
XtNoticeSignal(signal_id);
}
/* READ DATA XT SIGNAL HANDLER */
void read_data(XtPointer client_data, XtSignalId *id) {
/* ... READ DATA AND PROCESSING GOES HERE ... */
posx += 5;
printf("XT HANDLES SIGUSR1\n");
XmDrawingAreaCallbackStruct da_struct;
da_struct.reason = XmCR_EXPOSE;
da_struct.event = (XEvent *) NULL;
da_struct.window = XtWindow((Widget)client_data);
/* XtCallCallbacks((Widget)client_data, XmNexposeCallback, (XtPointer) &da_struct); */
}
The following causes the crash:
Display *dpy = event->xany.display;
Because event was set to NULL.
Also you may need to deal with gc by passing its address and receiving XmUserData as
GC *gc;
then using *gc,

XChangeProperty cannot write to clipboard

I want to put a string or bytes to the X11 selection so that when I switch to other applications, I can directly do a ctrl+p paste.
I try to follow the documentation of the X11 clipboard mechanism. If I understand correctly, I need to use XSetSelectionOwner to obtain the XA_CLIPBOARD selection and then use XChangeProperty to put my data to the clipboard.
Here is a simple snippet, but unfortunately it does not work:
// main.c
#include <stdlib.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xmu/Atoms.h>
int main() {
// try to write `hello` to the clipboard
const char *in = "hello\0";
const int n = 5;
Display* d = XOpenDisplay(0);
Window w = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, 1, 1, 0, 0, 0);
Atom XA_CLIPBOARD = XInternAtom(d, "CLIPBOARD", True);
XSetSelectionOwner(d, XA_CLIPBOARD, w, CurrentTime);
XEvent event;
XNextEvent(d, &event);
if (event.type != SelectionRequest) {
XCloseDisplay(d);
return 0;
}
if (event.xselectionrequest.selection != XA_CLIPBOARD) {
XCloseDisplay(d);
return 0;
}
XSelectionRequestEvent* req = &event.xselectionrequest;
XChangeProperty(d, req->requestor, req->property, XA_STRING, 8, PropModeReplace, (unsigned char *)in, n);
XEvent re = {0};
re.xselection.type = SelectionNotify;
re.xselection.display = req->display;
re.xselection.requestor = req->requestor;
re.xselection.selection = req->selection;
re.xselection.property = req->property;
re.xselection.target = req->target;
XSendEvent(d, req->requestor, 0, 0, &re); // event is sent, but data is not in my clipboard
XFlush(d);
XCloseDisplay(d);
return 0;
}
Compile: clang -o main main.c -lX11 -lXmu
What did I do wrong, and how to fix it?
XNextEvent retrieves any event that occured (input, pointer, configuration, property changes, ...).
So it is very unlikely that the first event, will be your desired event.
You have to iterate over the event queue to check if a SelectionRequest event occured.
Therefore you have to call XNextEvent in a loop and check everytime the event.type for the desired event.
Edit:
If you retrieve a ClientMessage event and the client data equals the Atom WM_DELETE, there was a request by the user to close the window (pressing the X). Other than that, you can quit whenever you want.
XEvent evt;
while (i_want_to_receive_and_process_events) {
XNextEvent(dpy, &evt);
switch (evt.type) {
case SelectionRequest:
if (evt.xselectionrequest.selection == XA_CLIPBOARD) {
// what you were looking for, do your thing
// i_want_to_receive_and_process_events = false (?)
}
break;
case ClientMessage:
if (evt.xclient.data.l[0] == WM_DELETE) {
i_want_to_receive_and_process_events = false;
}
break;
}
}

GLib GMainContext in a thread?

I have searched on stack overflow to find answer relevant to my problem. But i didn't find any answer.
I have a main thread (my main() function) which starts a thread. The new thread runs GMainLoop. In my main function, i keep adding sources by calling g_io_watch over some file descriptors. But i have getting garbage data if events are dispatched.
Below is a small part of the code that i am trying:
GMainLoop *loop;
gpointer event_loop_thread(gpointer arg)
{
g_main_loop_run(loop);
g_main_loop_unref(loop);
return NULL;
}
int init()
{
loop = g_main_loop_new(NULL, FALSE);
g_thread_new(NULL, event_loop_thread, NULL);
return 0;
}
gboolean __hci_service(GIOChannel *source, GIOCondition condition, gpointer data)
{
// Doing something
return FALSE;
}
int main()
{
init();
int _adapter_id = hci_devid("hci0");
int hci_dev = hci_open_dev(_adapter_id);
GIOChannel *p_hci_io = g_io_channel_unix_new(dev_id);
GIOCondition cond = (GIOCondition)(G_IO_IN);
g_io_add_watch(p_hci_io, cond, __hci_service, NULL);
while (true);
// I will close file descriptor
return 0;
}
However, if i try this code, then everything works as expected:
GMainLoop *loop;
gpointer event_loop_thread(gpointer arg)
{
g_main_loop_run(loop);
g_main_loop_unref(loop);
return NULL;
}
int init()
{
loop = g_main_loop_new(NULL, FALSE);
g_thread_new(NULL, event_loop_thread, NULL);
return 0;
}
gboolean __hci_service(GIOChannel *source, GIOCondition condition, gpointer data)
{
// Doing something
return FALSE;
}
int main()
{
// init();
int _adapter_id = hci_devid("hci0");
int hci_dev = hci_open_dev(_adapter_id);
GIOChannel *p_hci_io = g_io_channel_unix_new(dev_id);
GIOCondition cond = (GIOCondition)(G_IO_IN);
g_io_add_watch(p_hci_io, cond, __hci_service, NULL);
loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(loop);
g_main_loop_unref(loop);
while (true);
// I will close file descriptor
return 0;
}
Edit:
I have tried passing the default GMainContext of the main thread to the newly created thread. Have a look. Tell me if my approach is correct.
GMainLoop *loop;
gpointer event_loop_thread(gpointer arg)
{
GMainContext *context = (GMainContext *)arg;
loop = g_main_loop_new(context, FALSE);
g_main_context_push_thread_default(context);
g_main_loop_run(loop);
g_main_loop_unref(loop);
return NULL;
}
int init()
{
g_thread_new(NULL, event_loop_thread, (gpointer)g_main_context_default());
return 0;
}
gboolean __hci_service(GIOChannel *source, GIOCondition condition, gpointer data)
{
// Doing something
return FALSE;
}
int main()
{
init();
int _adapter_id = hci_devid("hci0");
int hci_dev = hci_open_dev(_adapter_id);
GIOChannel *p_hci_io = g_io_channel_unix_new(dev_id);
GIOCondition cond = (GIOCondition)(G_IO_IN);
g_io_add_watch(p_hci_io, cond, __hci_service, NULL);
//loop = g_main_loop_new(NULL, FALSE);
//g_main_loop_run(loop);
//g_main_loop_unref(loop);
while (true);
// I will close file descriptor
return 0;
}
You need to use GMainContext if you want to run a main loop from a thread. From the Glib's main loop documentation:
To allow multiple independent sets of sources to be handled in
different threads, each source is associated with a GMainContext. A
GMainContext can only be running in a single thread, but sources can
be added to it and removed from it from other threads.
When you create a main loop with g_main_loop_new(NULL, FALSE);, while it's handy to not specify any GMainContext, you need to pass a GMainContext from which you want your loop if you want to run the loop in a different thread. You can create a GMainContext with g_main_context_new() and pass it to g_main_loop_new(), or use g_main_context_get_thread_default() to get the default main context for the running thread.
g_io_add_watch() is yet another handy version of the function, which
Adds the GIOChannel into the default main loop context with the
default priority.
Unfortunately, there is no g_io_add_watch() variant function to specify a main context, you have to manually create a GSource from your GIOChannel, p_hci_io, and attach to your context via g_source_attach(). Note that g_io_add_watch_full() also works with the default main context.
The reason your second code works is that you created your main loop in the main thread which has your source attached.

How to fork a child proccess without stalling parent process in C

My GTK+2/C application uses Opencv library to grab frames continuously from the web camera feed and there's a UI button, which should calculate some information by grabbing a frames upon clicking.
This button click calculation takes 1-2 seconds and depend on the system resources. (on Raspberry pi, it takes 3-5 seconds)
Previously all functionalities (viewing + calculations) were inside the same main process. However when doing the calculation, whole app (video feed) used to freeze until the completion if the calculation.
I have implemented fork()to make child processes and keep the interprocess communications via pipe() to update the UI with calculated data.
However now the lagging time got lot bigger like 5-6 seconds.
below shows the sample code.
Is there any particular way to avoid this kind of lagging of the main process?
#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>
#include <diamond_calcs.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/videodev.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <opencv/cv.h>
#include <opencv/cxcore.h>
#include "highgui.h"
#include <X11/Xlib.h>
/* declaring the opencv capture variable */
CvCapture* capture, *fm_capture;
GdkPixbuf* pix;
IplImage* frame, *frame_sized;
gboolean expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data);
gboolean timeout(gpointer data);
gboolean autoon_clicked_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data[]);
// This function grabs a frame and recording some values. also this is the function called by the child process also.
gboolean frame_grabber(void)
{
/* setting up the edge ditection */
IplImage *frame_bgr;
if(frame){
//frame_bgr = cvQueryFrame( capture );
frame_bgr = frame_sized;
}
else{
frame_bgr = cvLoadImage("./data/media/novideo.png", 1);
}
/* Converting Color SPACE to greyscale */
int y_pos, x_pos;
int counter=0;
typedef enum { false, true } bool;
bool col_break = false;
for(int y=0;y<frame_bgr->height;y++)
{
for(int x=0;x<frame_bgr->width;x++)
{
CvScalar color = cvGet2D(frame_bgr, y, x);
if(color.val[0]<color_filter_thres){
if (y_pos == y){
counter++;
}
x_pos = x;
y_pos = y;
if (counter > 2){
//printf("top: %i, %i \n", x_pos, y_pos);
col_break = true;
break;
}
}
}
if (col_break){
break;
}
}
ruler_top_cal_preset = y_pos;
/* bottom ruler - scanning backwards*/
int y_pos_bot, x_pos_bot;
int counter_bot=0;
bool col_break2 = false;
for(int y_b=frame_bgr->height-1;y_b>0;y_b--)
{
for(int x_b=frame_bgr->width-1;x_b>0;x_b--)
{
CvScalar color_bot = cvGet2D(frame_bgr, y_b, x_b);
if(color_bot.val[0]<color_filter_thres){
if (y_pos_bot == y_b){
counter_bot++;
}
x_pos_bot = x_b;
y_pos_bot = y_b;
if (counter_bot > 2){
//printf("bottom: %i, %i \n", x_pos_bot, y_pos_bot);
col_break2 = true;
break;
}
}
}
if (col_break2){
break;
}
}
ruler_bottom_cal_preset = y_pos_bot;
dfactor_preset = ruler_bottom_cal_preset - ruler_top_cal_preset;
}
/*
* main
*
* Program begins here
*/
//####### Lot of processes going inside main, ignore it, just focus on the program flow
int main( int argc, char **argv )
{
/* variables structs to load the gtk_ based widgets and windows */
GtkBuilder *builder;
GtkWidget *window, *event_box, *drawing_area;
GError *error = NULL;
GError *error_settings = NULL;
GtkButton *button;
GtkLabel *label;
/* Init GTK+ */
gtk_init( &argc, &argv );
/* Create new GtkBuilder object */
builder = gtk_builder_new();
/* Load UI from file. If error occurs, report it and quit application. */
if( ! gtk_builder_add_from_file( builder, "file_glade.glade", &error ) )
{
g_warning( "%s", error->message );
g_free( error );
return( 1 );
}
/* Get main window pointer from UI */
window = GTK_WIDGET( gtk_builder_get_object( builder, "window1" ) );
/* creating the drawing area */
drawing_area = gtk_drawing_area_new();
gtk_container_add (GTK_CONTAINER(event_box), drawing_area);
gtk_widget_show (drawing_area);
/* connects the draw (expose-event) with the handler */
g_signal_connect (drawing_area, "expose-event",
G_CALLBACK (expose_event_callback), NULL);
gtk_label_set(labels[0], "Min :");
//####### Here goes the calculation clicking
gtk_signal_connect (GTK_OBJECT (gtk_builder_get_object( builder, "autoon_button" )), "clicked",
GTK_SIGNAL_FUNC (autoon_clicked_cb), labels);
/* Show window. All other widgets are automatically shown by GtkBuilder */
gtk_widget_show( window );
/* load last saved values */
file_load( spinners );
//#######
int fd;
if((fd = open("/dev/video0", O_RDONLY)) == -1){
printf("NO CAP\n");
capture = NULL;
}
else{
capture = cvCreateCameraCapture(0);
cvSetCaptureProperty(capture, CV_CAP_PROP_BRIGHTNESS, brght);
cvSetCaptureProperty(capture, CV_CAP_PROP_CONTRAST,contra);
cvSetCaptureProperty(capture, CV_CAP_PROP_SATURATION, satu);
cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, 800);
cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, 600);
// ## remove the forking from here and add that to the expose function
pid_t pID1 = vfork();
if (pID1 == 0) // child process
{
g_timeout_add (15, timeout, drawing_area);
//_exit(0);
}
else if ( pID1 < 0 ){
printf("Failed to fork video overlay child process \n");
exit(1);
}
}
/* Destroy builder, since we don't need it anymore */
g_object_unref( G_OBJECT( builder ) );
//g_object_unref( G_OBJECT( settings_builder ) );
//g_object_unref( G_OBJECT( about_builder ) );
/* Start main loop */
gtk_main();
return( 0 );
}
/* This function runs every x seconds and making the video feed */
/*
* Function: expose_event_callback
* --------------------
* When the widget is exposed of force to expose ths function handler will run to generate the video feed
*
* returns: none
*/
gboolean
expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
/* Capture a single frame from the video stream */
if (capture){
frame = cvQueryFrame( capture );
frame_sized = frame;
pix = gdk_pixbuf_new_from_data((guchar*) frame->imageData,
GDK_COLORSPACE_RGB, FALSE, frame->depth, frame->width,
frame->height, (frame->widthStep), NULL, NULL);
}
else{
printf("You've done it\n");
pix = gdk_pixbuf_new_from_file("./data/media/novideo.png", NULL);
}
/* putting the generated pix buff to the correct layout (GTK widget) */
gdk_draw_pixbuf(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE (widget)], pix, 0, 0, 0, 0,
-1, -1, GDK_RGB_DITHER_NONE, 0, 0); /* Other possible values are GDK_RGB_DITHER_MAX, GDK_RGB_DITHER_NORMAL */
return TRUE;
}
/*
* Function: timeout
* --------------------
* This function runs inbetween given time period. It calls the required functions to generate video and drawing
*
* returns: none
*/
gboolean timeout(gpointer data)
{
/* DEBUG print */
//printf("Time out Hello: %d\n", rand());
/* Redraws the Widget. Widget will call for the callback function again */
gtk_widget_queue_draw (data);
/* Starting the drawing function */
//ofdra(data,NULL);
return TRUE;
}
gboolean autoon_clicked_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data[])
{
char display1_entry_text[100];
int x = 1;
int pfds[2];
pipe(pfds);
pid_t pID = fork();
if (pID == 0) // child process
{
double ytops[10];
double ybots[10];
double min_diam;
double max_diam;
double avg_diam;
clock_t t1, t2,end_t; // clocking for debugging purposes
t2 = clock();
for(int i=0;i<15;i++)
{
//measure_clicked_callback(widget, NULL, data);
frame_grabber();
ytops[i] = ruler_top_cal_preset;
ybots[i] = ruler_bottom_cal_preset;
t1 = clock();
float x = (float)(t1-t2);
float y = x/CLOCKS_PER_SEC;
float z = y*1000;
t2 = t1;
}
/* sorting arrays for ascending order */
if(ybots && ytops){
array_sorter(ybots);
array_sorter(ytops);
min_diam = (ybots[0]-ytops[9])/scale_factor;
max_diam = (ybots[9]-ytops[0])/scale_factor;
avg_diam = (min_diam+max_diam)/2;
}
sprintf(display1_entry_text0,"Min : %.3g",min_diam);
write(pfds[1], display1_entry_text0, 100);
_exit(0);
}
else if(pID <0){
printf("Failed to fork \n");
exit(1);
}
else{
//parent process
char buf[100];
read(fpds[0], buf, 100);
//updates the gtk interface with read value
gtk_lable_set(data[0], buf);
wait(NULL);
}
return TRUE;
}
**Note I have pasted this code from the actual whole code, also this will not run. Pasted only here for give some idea about what I'm trying to achieve.
It looks like you have blocking read and wait calls in your parent process. In other words, despite the fact that you spawn a child process to perform the calculations, you still block your parent process till the child process completes. No parallelism is achieved.
I would suggest having a worker thread in the process that would do the calculations and notify the main thread when the results are available.
In the parent process/thread you should make the read end of the pipe non-blocking and register it with the event loop. Once the read end becomes ready for read, read all data in non-blocking mode.
Set a signal handler for SIGCHLD and call wait in it to release the child process resources.

XCB – Not receiving motion notify events on all windows

I am trying to be notified about any pointer motion. Since I don't want to run as the window manager, I need to set XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_POINTER_MOTION on all windows which I do both on startup and when I get a create notify event.
This seems to work fine in general and I receive motion notify events on all windows. However, somehow, this isn't true for Google Chrome windows. I checked the event mask by explicitly querying it afterwards and it is correctly set. I also don't see anything unusual in the propagation mask.
What could cause Google Chrome to not report motion notify events? AFAIK, the X protocol doesn't allow that except for active pointer grabs which Chrome surely doesn't have.
Here is how I register myself on all existing windows. I call register_events on the root window and whenever I receive a create notify event as well:
static void register_events(xcb_window_t window) {
xcb_void_cookie_t cookie = xcb_change_window_attributes_checked(connection,
window, XCB_CW_EVENT_MASK, (uint32_t[]) { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_LEAVE_WINDOW });
xcb_generic_error_t *error = xcb_request_check(connection, cookie);
if (error != NULL) {
xcb_disconnect(connection);
errx(EXIT_FAILURE, "could not subscribe to events on a window, bailing out");
}
}
static void register_existing_windows(void) {
xcb_query_tree_reply_t *reply;
if ((reply = xcb_query_tree_reply(connection, xcb_query_tree(connection, root), 0)) == NULL) {
return;
}
int len = xcb_query_tree_children_length(reply);
xcb_window_t *children = xcb_query_tree_children(reply);
for (int i = 0; i < len; i++) {
register_events(children[i]);
}
xcb_flush(connection);
free(reply);
}
The Chrome windows appear to be comprised of quite the tree of nested child windows. It appears you'll need to walk the tree of windows and monitor them all. This code picks up pointer motion events across the entirety of my Chrome windows:
#include <stdio.h>
#include <stdlib.h>
#include <xcb/xcb.h>
#include <X11/Xlib.h>
static void register_events(xcb_connection_t *conn,
xcb_window_t window) {
xcb_void_cookie_t cookie =
xcb_change_window_attributes_checked(conn,
window, XCB_CW_EVENT_MASK,
(uint32_t[]) {
XCB_EVENT_MASK_POINTER_MOTION });
xcb_generic_error_t *error = xcb_request_check(conn, cookie);
if (error != NULL) {
xcb_disconnect(conn);
exit(-1);
}
}
static void register_existing_windows(xcb_connection_t *conn,
xcb_window_t root) {
int i, len;
xcb_window_t *children;
xcb_query_tree_reply_t *reply;
if ((reply = xcb_query_tree_reply(conn,
xcb_query_tree(conn, root), 0))
== NULL)
{
return;
}
len = xcb_query_tree_children_length(reply);
children = xcb_query_tree_children(reply);
for (i = 0; i < len; i++) {
register_events(conn, children[i]);
register_existing_windows(conn, children[i]);
}
xcb_flush(conn);
}
void main(void) {
int i=0;
/* Open the connection to the X server */
xcb_connection_t *conn = xcb_connect (NULL, NULL);
/* Get the first screen */
xcb_screen_t *screen = xcb_setup_roots_iterator (xcb_get_setup (conn)).data;
register_existing_windows(conn, screen->root);
while(1) {
xcb_generic_event_t *evt;
evt = xcb_wait_for_event(conn);
printf("%i\n", i++);
}
}
(That's just intended as proof of concept, and not very nice.)
While #Jay Kominek's answer was helpful and valid, I've come to realize now that using the Xinput extension provides a much better approach as it won't interfere with applications whatsoever.
Simply selecting on the entire tree causes all kinds of issues, e.g., hover doesn't work in Chrome anymore.
xcb provides xcb_grab_pointer to capture pointer event without registe on specific window.
#include <stdlib.h>
#include <stdio.h>
#include <xcb/xcb.h>
void
print_modifiers (uint32_t mask)
{
const char **mod, *mods[] = {
"Shift", "Lock", "Ctrl", "Alt",
"Mod2", "Mod3", "Mod4", "Mod5",
"Button1", "Button2", "Button3", "Button4", "Button5"
};
printf ("Modifier mask: ");
for (mod = mods ; mask; mask >>= 1, mod++)
if (mask & 1)
printf(*mod);
putchar ('\n');
}
int
main ()
{
xcb_connection_t *c;
xcb_screen_t *screen;
xcb_window_t win;
xcb_generic_event_t *e;
uint32_t mask = 0;
/* Open the connection to the X server */
c = xcb_connect (NULL, NULL);
/* Get the first screen */
screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
mask = XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION;
xcb_grab_pointer(c, false, screen->root, mask, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_CURRENT_TIME);
xcb_flush (c);
while ((e = xcb_wait_for_event (c))) {
switch (e->response_type & ~0x80) {
case XCB_BUTTON_PRESS: {
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)e;
print_modifiers(ev->state);
switch (ev->detail) {
case 4:
printf ("Wheel Button up in window %ld, at coordinates (%d,%d)\n",
ev->event, ev->event_x, ev->event_y);
break;
case 5:
printf ("Wheel Button down in window %ld, at coordinates (%d,%d)\n",
ev->event, ev->event_x, ev->event_y);
break;
default:
printf ("Button %d pressed in window %ld, at coordinates (%d,%d)\n",
ev->detail, ev->event, ev->event_x, ev->event_y);
}
break;
}
case XCB_BUTTON_RELEASE: {
xcb_button_release_event_t *ev = (xcb_button_release_event_t *)e;
print_modifiers(ev->state);
printf ("Button %d released in window %ld, at coordinates (%d,%d)\n",
ev->detail, ev->event, ev->event_x, ev->event_y);
break;
}
case XCB_MOTION_NOTIFY: {
xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)e;
printf ("Mouse moved in window %ld, at coordinates (%d,%d)\n",
ev->event, ev->event_x, ev->event_y);
break;
}
default:
/* Unknown event type, ignore it */
printf("Unknown event: %d\n", e->response_type);
break;
}
/* Free the Generic Event */
free (e);
}
return 0;
}

Resources