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;
}
}
Related
I need to be notified when mouse enters or leaves a window (any window, that's why I'm grabbing on the root window). I have a solution using xcb_input_xi_grab_device() but it works only half way. When I run the app, I do get enter/leave events but the mouse pointer disappears and it consumes all mouse events and other windows stop (children of the root window) reacting to clicks.
Using XCB_INPUT_GRAB_OWNER_OWNER should allow passing-through the events but it doesn't work.
app.c:
#include <stdio.h>
#include <xcb/xcb.h>
#include <xcb/xinput.h>
xcb_input_device_id_t find_device(xcb_connection_t *conn)
{
xcb_input_xi_query_device_reply_t *reply = xcb_input_xi_query_device_reply(
conn, xcb_input_xi_query_device(conn, XCB_INPUT_DEVICE_ALL), NULL);
xcb_input_xi_device_info_iterator_t iter =
xcb_input_xi_query_device_infos_iterator(reply);
xcb_input_device_id_t device_id;
int found = 0;
while (iter.rem) {
xcb_input_xi_device_info_t *device = iter.data;
switch (device->type) {
case XCB_INPUT_DEVICE_TYPE_MASTER_POINTER:
case XCB_INPUT_DEVICE_TYPE_SLAVE_POINTER:
case XCB_INPUT_DEVICE_TYPE_FLOATING_SLAVE:
device_id = device->deviceid;
found = 1;
break;
}
if (found) {
break;
}
xcb_input_xi_device_info_next(&iter);
}
free(reply);
return device_id;
}
int main()
{
xcb_connection_t *conn = xcb_connect(NULL, NULL);
xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
uint32_t mask =
XCB_INPUT_XI_EVENT_MASK_ENTER | XCB_INPUT_XI_EVENT_MASK_LEAVE;
xcb_generic_error_t *error;
xcb_input_xi_grab_device_reply_t *reply = xcb_input_xi_grab_device_reply(
conn,
xcb_input_xi_grab_device(conn, screen->root, XCB_CURRENT_TIME,
XCB_CURSOR_NONE, find_device(conn),
XCB_INPUT_GRAB_MODE_22_ASYNC,
XCB_INPUT_GRAB_MODE_22_ASYNC,
XCB_INPUT_GRAB_OWNER_OWNER, 1, &mask),
&error);
free(reply);
if (error) {
printf("failed to grab device\n");
free(error);
return -1;
}
xcb_flush(conn);
xcb_generic_event_t *event;
while ((event = xcb_wait_for_event(conn))) {
xcb_ge_event_t *generic_event = event;
switch (generic_event->event_type) {
case XCB_INPUT_ENTER:
printf("enter\n");
break;
case XCB_INPUT_LEAVE:
printf("leave\n");
break;
}
free(event);
}
return -1; // never reached
}
cc -O2 app.c -o app `pkg-config --cflags --libs xcb xcb-xinput`
I also tried to achieve it with xcb_grab_button(), xcb_input_xi_select_events(), xcb_input_xi_passive_grab_device() or xcb_grab_pointer() but then the problem is the same. Either the mouse events are not passed-through or I don't receive enter/leave events at all.
I would like to open a "dock type" window without its title bar with x11 in C and I want to capture a key press (Ctrl-q) to close it. The actual code does not capture the key presses to the active window as they are all sent and printed in the terminal. Almost all the Ctrl key presses are somehow not detected. The only key presses that actually work on the opened window are Ctrl-c. I am aware of the _MOTIF_WM_HINTS property but it has the disadvantage to display a window icon in the panel to show a new opened window.
How can I have a "dock type" window while not losing the ability to detect key presses to the active window?
Here is the code basically copied from two other posts here and here
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
int main(int argc, char **argv)
{
Display *dpy = XOpenDisplay(0);
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);
//Tell X Server to send MapNotify events
XSelectInput(dpy, w, StructureNotifyMask | KeyPressMask);
Atom window_type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
long value = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
XChangeProperty(dpy, w, window_type, 4, 32,
PropModeReplace,
(unsigned char *)&value, 1);
//Make window appear
XMapWindow(dpy, w);
//Graphics Context
GC gc = XCreateGC(dpy, w, 0, 0);
//Set white color for drawing
XSetForeground(dpy, gc, whiteColor);
//Wait for the MapNotify event
for(;;) {
XEvent e;
XNextEvent(dpy, &e);
if (e.type == MapNotify) {
break;
}
}
//Draw the line
XDrawLine(dpy, w, gc, 10, 60, 180, 20);
//Send the "DrawLine" request to the server
XFlush(dpy);
char text[255];
XEvent e;
KeySym key;
int numKeys = 0;
for(;;) {
XNextEvent(dpy, &e);
if(e.type == KeyPress) {
if((numKeys = XLookupString(&e.xkey, text, 255, &key, 0))) {
if(e.xkey.state == ControlMask && key == XK_q) {
printf("CTRL-Q\n");
break;
}
}
}
}
if (dpy && gc) XFreeGC(dpy, gc);
if (dpy && w) XDestroyWindow(dpy, w);
if (dpy) XCloseDisplay(dpy);
return 0;
}
Compiled with:
gcc main.c -L/usr/X11R6/lib -lX11 -lm && ./a.out
I've also looked at the _NET_WM_STRUT_PARTIAL property but I haven't been succesful at implementing it.
This is a working example of X11 code that handles Ctrl-q event to quit application:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
void exitOnCondition(char cond, const char *msg, int exitCode, Display *dpy, Window *w, GC *gc) {
if(cond) {
printf("%s\n", msg);
if(dpy && gc) XFreeGC(dpy, *gc);
if(dpy && w) XDestroyWindow(dpy, *w);
if(dpy) XCloseDisplay(dpy);
exit(exitCode);
}
}
int main(int argc, char **argv) {
Display *dpy = XOpenDisplay(0);
exitOnCondition(dpy == 0, "Error: XOpenDisplay failed", -1, dpy, 0, 0);
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);
//Tell X Server to send MapNotify events
XSelectInput(dpy, w, StructureNotifyMask | KeyPressMask);
//Make window appear
XMapWindow(dpy, w);
//Graphics Context
GC gc = XCreateGC(dpy, w, 0, 0);
//Set white color for drawing
XSetForeground(dpy, gc, whiteColor);
//Wait for the MapNotify event
for(;;) {
XEvent e;
XNextEvent(dpy, &e);
if (e.type == MapNotify) {
break;
}
}
//Draw the line
XDrawLine(dpy, w, gc, 10, 60, 180, 20);
//Send the "DrawLine" request to the server
XFlush(dpy);
char text[255];
XEvent e;
KeySym key;
int numKeys = 0;
for(;;) {
XNextEvent(dpy, &e);
if(e.type == KeyPress) {
//With modifier XLookupString will return garbage(?) in text[0] and key as latin1
if((numKeys = XLookupString(&e.xkey, text, 255, &key, 0))) {
printf("lookup returned:\n");
for(int i = 0; i < numKeys; i++) {
printf("text[%d]=%x\n", i, text[i]);
}
if(e.xkey.state == ControlMask && key == XK_q) {
exitOnCondition(1, "C-Q pressed", 0, dpy, &w, &gc);
}
}
}
}
XFreeGC(dpy, gc);
XDestroyWindow(dpy,w);
XCloseDisplay(dpy);
}
Will this code correctly handle Ctrl-q event on any system?
Can I use e.xkey.state to check for Ctrl modifier after XLookupString for any keyboard layout even when Ctrl is rebound to CAPS Lock(or anything else)?
Why does XLookupString return one symbol text[0]==0x11 for Ctrl-q event and not text[0]==CtrlModifierCode text[1]=='q'?
XLookupString returns a sequence of ISO-8859-1 characters (at least according to the manual I have; I do not know how up-to-date it is). There is no character code in ISO-8859-1 for the "ctrl" key by itself. Strictly speaking, there isn't one for the ctrl-q combination either, but tradition dictates that ctrl + (A...Z) map to the 1...26 range.
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;
}
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;
}