Given the code:
/*
* XWindows study on if we can turn the frame on and off.
*/
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAPIT
#define REPEAT
void waitxevt(Display* d, int type)
{
XEvent e; /* XEvent holder */
do { XNextEvent(d, &e); } while (e.type != type);
}
void frame(Display* d, Window w, int e)
{
Atom window_type;
long value;
#ifdef MAPIT
XUnmapWindow(d, w);
waitxevt(d, UnmapNotify);
#endif
window_type = XInternAtom(d, "_NET_WM_WINDOW_TYPE", False);
if (e) value = XInternAtom(d, "_NET_WM_WINDOW_TYPE_NORMAL", False);
else value = XInternAtom(d, "_NET_WM_WINDOW_TYPE_DOCK", False);
XChangeProperty(d, w, window_type, XA_ATOM, 32, PropModeReplace, (unsigned char *) &value, 1);
#ifdef MAPIT
XMapWindow(d, w);
waitxevt(d, MapNotify);
#endif
}
int main(void) {
Display* d;
Window w;
XEvent e;
const char* msg = "Hello, World!";
int s;
GC gracxt;
int frmenb = 0;
d = XOpenDisplay(NULL);
if (d == NULL) {
fprintf(stderr, "Cannot open display\n");
exit(1);
}
s = DefaultScreen(d);
gracxt = XDefaultGC(d, s);
w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 640, 480, 5,
BlackPixel(d, s), WhitePixel(d, s));
XSelectInput(d, w, ExposureMask|KeyPressMask|StructureNotifyMask);
XMapWindow(d, w);
waitxevt(d, MapNotify);
while (1) {
XNextEvent(d, &e);
if (e.type == Expose) XDrawString(d, w, gracxt, 10, 50, msg, strlen(msg));
if (e.type == KeyPress) {
frame(d, w, frmenb);
frmenb = !frmenb;
#ifdef REPEAT
frame(d, w, frmenb);
frmenb = !frmenb;
#endif
}
}
XCloseDisplay(d);
return 0;
}
The purpose of the code is to turn the frame on and off for the window w. With no map/unmap operation, The "off" call works correctly, and the window changes (onscreen) to have no frame, and is not sizable, and the titlebar no longer appears.
However, on turning it back on using the routine, the titlebar is not visible, but it is sizable. If you resize the window, the titlebars reappear.
The suggestion was made to unmap the window before setting the property and remap after. The result of this is that the window without frame no longer can have focus, and thus cannot get a key to change back. I put in a "repeat" flag to make it immediately revert back to full frame mode, and thus prove that the return to framing works. It does. Another side effect is that the position of the window gets reset after the map operation, but I can compensate for that.
This is on Ubuntu 20.04.
What is the purpose? This code is part of a toolkit to port between Windows and Linux. The frame on/off is used for two purposes (so far):
To make component windows (widgets) without frames.
To toggle the frame. For example, I have a clock program that if you click on it, it will remove the frame, making it both smaller and immobile. Another click restores it.
The first one works now, since it only turns off the frame once, and that happens before mapping. The second one does not work.
New program, now works, using the suggestions:
/*
* XWindows study on if we can turn the frame on and off.
*/
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct MwmHints {
unsigned long flags;
unsigned long functions;
unsigned long decorations;
long input_mode;
unsigned long status;
};
enum {
MWM_HINTS_FUNCTIONS = (1L << 0),
MWM_HINTS_DECORATIONS = (1L << 1),
MWM_FUNC_ALL = (1L << 0),
MWM_FUNC_RESIZE = (1L << 1),
MWM_FUNC_MOVE = (1L << 2),
MWM_FUNC_MINIMIZE = (1L << 3),
MWM_FUNC_MAXIMIZE = (1L << 4),
MWM_FUNC_CLOSE = (1L << 5)
};
void frame(Display* d, Window w, int e)
{
Atom mwmHintsProperty;
struct MwmHints hints;
mwmHintsProperty = XInternAtom(d, "_MOTIF_WM_HINTS", 0);
hints.flags = MWM_HINTS_DECORATIONS;
hints.decorations = !!e;
XChangeProperty(d, w, mwmHintsProperty, mwmHintsProperty, 32,
PropModeReplace, (unsigned char *)&hints, 5);
}
int main(void) {
Display* d;
Window w;
XEvent e;
const char* msg = "Hello, World!";
int s;
GC gracxt;
int frmenb = 0;
d = XOpenDisplay(NULL);
if (d == NULL) {
fprintf(stderr, "Cannot open display\n");
exit(1);
}
s = DefaultScreen(d);
gracxt = XDefaultGC(d, s);
w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 640, 480, 5,
BlackPixel(d, s), WhitePixel(d, s));
XSelectInput(d, w, ExposureMask|KeyPressMask|StructureNotifyMask);
XMapWindow(d, w);
while (1) {
XNextEvent(d, &e);
if (e.type == Expose) XDrawString(d, w, gracxt, 10, 50, msg, strlen(msg));
if (e.type == KeyPress) {
frame(d, w, frmenb);
frmenb = !frmenb;
}
}
XCloseDisplay(d);
return 0;
}
Related
Given the sample program:
/*
* Study for multiple windows.
*/
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
Window w1, w2;
XEvent e;
const char* msg1 = "Hello, window 1";
const char* msg2 = "Hello, window 2";
int s1;
int s2;
GC gracxt1;
GC gracxt2;
XFontStruct* font;
XFontStruct* font2;
Display* d1;
Display* d2;
d1 = XOpenDisplay(NULL);
if (d1 == NULL) {
fprintf(stderr, "Cannot open display\n");
exit(1);
}
printf("d1: %p\n", d1);
d2 = XOpenDisplay(NULL);
if (d2 == NULL) {
fprintf(stderr, "Cannot open display\n");
exit(1);
}
printf("d2: %p\n", d2);
s1 = DefaultScreen(d1);
s2 = DefaultScreen(d1);
printf("s1: %d\n", s1);
printf("s2: %d\n", s2);
gracxt1 = XDefaultGC(d1, s1);
#if 0
gracxt2 = XDefaultGC(d2, s2);
#else
gracxt2 = XDefaultGC(d1, s2);
#endif
font = XLoadQueryFont(d1,
"-bitstream-courier 10 pitch-bold-r-normal--0-0-200-200-m-0-iso8859-1");
if (!font) {
fprintf(stderr, "*** No font ***\n");
exit(1);
}
XSetFont(d1, gracxt1, font->fid);
font2 = XLoadQueryFont(d1,
"-bitstream-courier 10 pitch-bold-r-normal--0-0-400-400-m-0-iso8859-1");
if (!font2) {
fprintf(stderr, "*** No font ***\n");
exit(1);
}
XSetFont(d1, gracxt2, font2->fid);
w1 = XCreateSimpleWindow(d1, RootWindow(d1, s1), 10, 10, 640, 480, 5,
BlackPixel(d1, s1), WhitePixel(d1, s1));
XSelectInput(d1, w1, ExposureMask|KeyPressMask|PointerMotionMask|StructureNotifyMask);
XMapWindow(d1, w1);
w2 = XCreateSimpleWindow(d1, RootWindow(d1, s1), 10, 10, 640, 480, 5,
BlackPixel(d1, s2), WhitePixel(d1, s2));
XSelectInput(d1, w2, ExposureMask|KeyPressMask|PointerMotionMask|StructureNotifyMask);
XMapWindow(d1, w2);
while (1) {
XNextEvent(d1, &e);
if (e.type == Expose) {
if (e.xany.window == w1) XDrawString(d1, e.xany.window, gracxt1, 10, 50, msg1, strlen(msg1));
else XDrawString(d1, e.xany.window, gracxt2, 10, 50, msg2, strlen(msg2));
}
if (e.type == KeyPress) break; /* exit on any key */
}
XCloseDisplay(d1);
XCloseDisplay(d2);
return 0;
}
It sets up two windows, and selects two different fonts, one for each window, and prints. My understanding of XWindows is that the font is selected into the graphics context, the GC. However, I get two different behaviors depending on which Display* is used:
#if 1
IE, each window with a different size font, or:
#if 0
I would think, and the documentation strongly implies, that the font is selected to the context gracxt1 or gracxt2 by the XSetFont() call. Thus I started this example with two GCs, but one Display* and one screen select. However, I don't get the behavior of font select per window until I give each window its own Display*.
The prints show:
d1: 0x55a5694692a0
d2: 0x55a569477170
s1: 0
s2: 0
For both, distinct Display* but same screen indexes. So where is the font select for the window stored? The Display*?
The documentation says "the font is selected to the GC, and the display just specifies the connection to the X server".
OS: Ubuntu 20.04
I printed the GContext numbers for each of gracxt1 and gracxt2:
#if 1 (working case)
d1: 0x55b4ae3d82a0
d2: 0x55b4ae3e6170
s1: 0
s2: 0
GCContext 1: 69206016
GCContext 2: 71303168
#if 0 (failing case)
d1: 0x55b4149672a0
d2: 0x55b414975170
s1: 0
s2: 0
GCContext 1: 69206016
GCContext 2: 69206016
The program refactored to function correctly:
/*
* Study for multiple windows.
*/
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
Window w1, w2;
GC gracxt1, gracxt2;
XEvent e;
const char* msg1 = "Hello, window 1";
const char* msg2 = "Hello, window 2";
int s;
XFontStruct* font;
Display* d;
d = XOpenDisplay(NULL);
if (d == NULL) {
fprintf(stderr, "Cannot open display\n");
exit(1);
}
s = DefaultScreen(d);
s = DefaultScreen(d);
w1 = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 640, 480, 5,
BlackPixel(d, s), WhitePixel(d, s));
XSelectInput(d, w1, ExposureMask|KeyPressMask|PointerMotionMask|StructureNotifyMask);
XMapWindow(d, w1);
gracxt1 = XCreateGC(d, w1, 0, NULL);
w2 = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 640, 480, 5,
BlackPixel(d, s), WhitePixel(d, s));
XSelectInput(d, w2, ExposureMask|KeyPressMask|PointerMotionMask|StructureNotifyMask);
XMapWindow(d, w2);
gracxt2 = XCreateGC(d, w2, 0, NULL);
font = XLoadQueryFont(d,
"-bitstream-courier 10 pitch-bold-r-normal--0-0-200-200-m-0-iso8859-1");
if (!font) {
fprintf(stderr, "*** No font ***\n");
exit(1);
}
XSetFont(d, gracxt1, font->fid);
font = XLoadQueryFont(d,
"-bitstream-courier 10 pitch-bold-r-normal--0-0-400-400-m-0-iso8859-1");
if (!font) {
fprintf(stderr, "*** No font ***\n");
exit(1);
}
XSetFont(d, gracxt2, font->fid);
while (1) {
XNextEvent(d, &e);
if (e.type == Expose) {
if (e.xany.window == w1) XDrawString(d, e.xany.window, gracxt1, 10, 50, msg1, strlen(msg1));
else XDrawString(d, e.xany.window, gracxt2, 10, 50, msg2, strlen(msg2));
}
if (e.type == KeyPress) break; /* exit on any key */
}
XCloseDisplay(d);
return 0;
}
The font is part of the graphics context. Every graphics context has its own independent font setting.
I think your post has reversed the behaviors of the #if 0 and #if 1 cases. If so, then the behaviors you describe make perfect sense.
XOpenDisplay creates one graphics context per screen after connecting to the server. So d1 has a graphic context for screen 0, and d2 has a separate graphics context for screen 0. Therefore:
In both cases, gracxt1 uses the graphics context of d1 screen 0.
In the #if 0 case, gracxt2 uses the graphics context of d2 screen 0, which is separate from gracxt1. You set each GC's font to a different value. This is the behavior we see in your first screen shot: the two GCs have separate fonts.
In the #if 1 case, gracxt2 uses the graphics context of d1 screen 0, which is the same as gracxt1. When you set gracxt2's font, you are also setting gracxt1's font, overriding the earlier setting (because they are the same GC). This is the behavior we see in your second screen shot.
I need to create a Tray Icon for my application using purely the Xlib. After some searching, reading the XEmbed documentation and some stuff on SO, this is what the code looks like so far (mostly copy & paste). I understand what it does, but not why. It is creating a tray icon appearantly, but only 1px wide in my mate-panels area. What am I doing wrong? Why is it not the size of a normal tray icon?
Here is my code:
#include <X11/Xutil.h>
#include <string.h>
#define MIN(A, B) ((A) < (B) ? (A) : (B))
/* --------- XEMBED and systray stuff */
#define SYSTEM_TRAY_REQUEST_DOCK 0
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
#define SYSTEM_TRAY_CANCEL_MESSAGE 2
static int trapped_error_code = 0;
static int (*old_error_handler) (Display *, XErrorEvent *);
static int
error_handler(Display *display, XErrorEvent *error) {
trapped_error_code = error->error_code;
return 0;
}
void
trap_errors(void) {
trapped_error_code = 0;
old_error_handler = XSetErrorHandler(error_handler);
}
int
untrap_errors(void) {
XSetErrorHandler(old_error_handler);
return trapped_error_code;
}
void
send_systray_message(Display* dpy, long message, long data1, long data2, long data3) {
XEvent ev;
Atom selection_atom = XInternAtom (dpy,"_NET_SYSTEM_TRAY_S0",False);
Window tray = XGetSelectionOwner (dpy,selection_atom);
if ( tray != None)
XSelectInput (dpy,tray,StructureNotifyMask);
memset(&ev, 0, sizeof(ev));
ev.xclient.type = ClientMessage;
ev.xclient.window = tray;
ev.xclient.message_type = XInternAtom (dpy, "_NET_SYSTEM_TRAY_OPCODE", False );
ev.xclient.format = 32;
ev.xclient.data.l[0] = CurrentTime;
ev.xclient.data.l[1] = message;
ev.xclient.data.l[2] = data1; // <--- your window is only here
ev.xclient.data.l[3] = data2;
ev.xclient.data.l[4] = data3;
trap_errors();
XSendEvent(dpy, tray, False, NoEventMask, &ev);
XSync(dpy, False);
usleep(10000);
if (untrap_errors()) {
/* Handle errors */
}
}
/* ------------ Regular X stuff */
int
main(int argc, char **argv) {
int width, height;
XWindowAttributes wa;
XEvent ev;
Display *dpy;
int screen;
Window root, win;
/* init */
if (!(dpy=XOpenDisplay(NULL)))
return 1;
screen = DefaultScreen(dpy);
root = RootWindow(dpy, screen);
if(!XGetWindowAttributes(dpy, root, &wa))
return 1;
width = height = MIN(wa.width, wa.height);
/* create window */
win = XCreateSimpleWindow(dpy, root, 0, 0, width, height, 0, 0, 0x7F7F997F);
send_systray_message(dpy, SYSTEM_TRAY_REQUEST_DOCK, win, 0, 0); // pass win only once
XMapWindow(dpy, win);
XSync(dpy, False);
/* run */
while(1) {
while(XPending(dpy)) {
XNextEvent(dpy, &ev); /* just waiting until we error because window closed */
}
}
}
I am thankful for everything that will point me in the right direction, even if its just some documentation that I have not found so far.
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 want to execute code when user will use mouse wheel. I wrote simple example application and it is not working. Why my application is not reacting on mouse wheel?
#include <X11/Xlib.h>
#include <X11/X.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
Display *d;
Window w;
XEvent e;
char *msg = "Hello, World!";
int s;
d = XOpenDisplay(NULL);
if (d == NULL) {
fprintf(stderr, "Cannot open display\n");
exit(1);
}
s = DefaultScreen(d);
w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1,
BlackPixel(d, s), WhitePixel(d, s));
XSelectInput(d, w, ExposureMask | ButtonPressMask);
XMapWindow(d, w);
while (1) {
XNextEvent(d, &e);
if (e.type == Expose) {
XFillRectangle(d, w, DefaultGC(d, s), 20, 20, 10, 10);
XDrawString(d, w, DefaultGC(d, s), 10, 50, msg, strlen(msg));
}
if (e.type == ButtonPress){
switch (e.xbutton.button){
case Button4:
printf("Scrolled up");
break;
case Button5:
printf("Scrolled down");
break;
default:
printf("cos");
}
}
}
XCloseDisplay(d);
return 0;
}
your code is correct, the reason you don't see results immediately is because fprintf buffers data until newline. Replace your debug lines with something like printf("Scrolled up\n"); and you'll see results immediately. Alternatively you might do setbuf(stdout, NULL); to disable buffering.
See this related SO question: Why does printf not flush after the call unless a newline is in the format string?