GUI stuff
https://www.youtube.com/watch?v=d2E7ryHCK08
Doc: https://www.x.org/releases/X11R7.7/doc/libX11/libX11/libX11.html
simple window
#include <X11/Xlib.h>
int main() {
XEvent event;
Display* display = XOpenDisplay(NULL);
Window w = XCreateSimpleWindow(display, DefaultRootWindow(display), 50, 50, 250, 250, 1, BlackPixel(display, 0), WhitePixel(display, 0));
XMapWindow(display, w);
while (1) {
XNextEvent(display, &event);
}
}
gcc x.c -lX11
translucent background
edit attr.background_pixel
#include <X11/Xlib.h>
#include <X11/Xutil.h>
int main() {
XEvent event;
Display* display = XOpenDisplay(NULL);
XVisualInfo vinfo;
XMatchVisualInfo(display, DefaultScreen(display), 32, TrueColor, &vinfo);
XSetWindowAttributes attr;
attr.colormap = XCreateColormap(display, DefaultRootWindow(display), vinfo.visual, AllocNone);
attr.background_pixel = 0x00000000;
Window w = XCreateWindow(display, DefaultRootWindow(display), 0, 0, 300, 200, 0, vinfo.depth, InputOutput, vinfo.visual, CWColormap | CWBorderPixel | CWBackPixel, &attr);
XMapWindow(display, w);
while (1) {
XNextEvent(display, &event);
}
}
Class hint properties
https://tronche.com/gui/x/xlib/ICC/client-to-window-manager/wm-class.html#XClassHint
#include <X11/Xlib.h>
#include <X11/Xutil.h>
int main() {
XEvent event;
Display* display = XOpenDisplay(NULL);
Window w = XCreateSimpleWindow(display, DefaultRootWindow(display), 50, 50, 250, 250, 1, BlackPixel(display, 0), WhitePixel(display, 0));
XMapWindow(display, w);
// set CLASS property
XClassHint* class_hint = XAllocClassHint();
class_hint->res_class = "myapp";
class_hint->res_name= "myapp";
XSetClassHint(display, w, class_hint);
while (1) {
XNextEvent(display, &event);
}
}
$ xprop | grep CLASS
WM_CLASS(STRING) = "myapp", "myapp"
text
https://stackoverflow.com/questions/44476594/x11-why-i-cant-draw-any-text
Create a window unaffected by dwm tiling
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
int main() {
Display* display = XOpenDisplay(NULL);
int screen = DefaultScreen(display);
Window root = RootWindow(display, screen);
XSetWindowAttributes wa = {
.override_redirect = True,
};
Window w = XCreateWindow(display, root, 50, 50, 500, 500, 0, DefaultDepth(display, screen),
InputOutput, DefaultVisual(display, screen),
CWOverrideRedirect | CWBackPixel, &wa);
XMapRaised(display, w);
XClassHint ch = {"dwm", "dwm"};
XSetClassHint(display, w, &ch);
XEvent event;
while (1) {
XNextEvent(display, &event);
}
}
Images
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
// Window size
int height = 256, width = 256;
XVisualInfo vinfo;
XImage *CreateTrueColorImage(Display *display, Visual *visual)
{
int i, j;
char *image32=(char *)malloc(width*height*4);
char *p=image32;
for(i=0; i<width; i++)
{
for(j=0; j<height;j++)
{
*p++ = 0x00; // B
*p++ = 0x00; // G
*p++ = 0xff; // R
*p++ = 0xff; // alpha
}
}
return XCreateImage(display, vinfo.visual, vinfo.depth,
ZPixmap, 0, image32, width, height, 32, 0);
}
int main(int argc, char **argv)
{
XImage *ximage;
Display *display = XOpenDisplay(NULL);
Visual *visual = DefaultVisual(display, 0);
XMatchVisualInfo(display, DefaultScreen(display), 32, TrueColor, &vinfo);
XSetWindowAttributes attr;
attr.colormap = XCreateColormap(display, DefaultRootWindow(display),
vinfo.visual, AllocNone);
attr.border_pixel = 0;
attr.background_pixel = 0x00000000;
Window window = XCreateWindow(display, DefaultRootWindow(display), 0, 0,
width, height, 0, vinfo.depth, InputOutput, vinfo.visual,
CWColormap | CWBorderPixel | CWBackPixel, &attr);
ximage = CreateTrueColorImage(display, vinfo.visual);
XSelectInput(display, window, ButtonPressMask|ExposureMask);
XMapWindow(display, window);
GC gc = XCreateGC(display, window, 0, 0);
XEvent event;
while (1) {
XNextEvent(display, &event);
XPutImage(display, window, gc, ximage, 0, 0, 0, 0, width, height);
}
}
Transparant and unaffected by dwm tiling
-lX11 -lXrender
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xrender.h>
#include <stdio.h>
#include <unistd.h>
int main() {
Display* dpy = XOpenDisplay(NULL);
if (!dpy) {
fprintf(stderr, "Failed to open display\n");
return 1;
}
int screen = DefaultScreen(dpy);
Window root = RootWindow(dpy, screen);
// Find a 32-bit TrueColor visual (ARGB)
XVisualInfo vinfo;
if (!XMatchVisualInfo(dpy, screen, 32, TrueColor, &vinfo)) {
fprintf(stderr, "No 32-bit TrueColor visual available\n");
return 1;
}
// Setup window attributes
XSetWindowAttributes attrs;
attrs.colormap = XCreateColormap(dpy, root, vinfo.visual, AllocNone);
attrs.background_pixel = 0x00000000;
attrs.border_pixel = 0;
attrs.override_redirect = True;
const int win_w = 300;
const int win_h = 60;
int screen_height = DisplayHeight(dpy, screen);
int screen_width = DisplayWidth(dpy, screen);
Window win = XCreateWindow(
dpy, root,
screen_width-win_w, screen_height-win_h, win_w, win_h,
0, vinfo.depth, InputOutput, vinfo.visual,
CWColormap | CWBackPixel | CWBorderPixel | CWOverrideRedirect,
&attrs
);
XMapWindow(dpy, win);
// Create XRender Picture for drawing
XRenderPictFormat* fmt = XRenderFindVisualFormat(dpy, vinfo.visual);
Picture pict = XRenderCreatePicture(dpy, win, fmt, 0, NULL);
XRenderColor background = {0, 0, 0, 0}; // transparant
//XRenderColor background = {0, 0xffff, 0, 0xffff};
XRenderColor red = {0xffff, 0x0000, 0x0000, 0xffff}; // solid red
int x = 0;
int speed = 4;
while (1) {
// Clear window to transparent
XRectangle clear_rect = {0, 0, win_w, win_h};
XRenderFillRectangles(dpy, PictOpSrc, pict, &background, &clear_rect, 1);
// Draw moving red rectangle
XRectangle red_rect = {x, win_h / 2 - 20, 40, 40};
XRenderFillRectangles(dpy, PictOpOver, pict, &red, &red_rect, 1);
XFlush(dpy);
// Update position
x += speed;
if (x < 0 || x > win_w - 40) speed = -speed;
usleep(16000); // ~60 FPS
}
// Cleanup (never actually reached)
XRenderFreePicture(dpy, pict);
XCloseDisplay(dpy);
return 0;
}
With an image:
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xrender.h>
#include <stdio.h>
#include <unistd.h>
// wget https://github.com/nothings/stb/blob/master/stb_image.h
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
int main() {
Display* dpy = XOpenDisplay(NULL);
if (!dpy) {
fprintf(stderr, "Failed to open display\n");
return 1;
}
int screen = DefaultScreen(dpy);
Window root = RootWindow(dpy, screen);
// Find a 32-bit TrueColor visual (ARGB)
XVisualInfo vinfo;
if (!XMatchVisualInfo(dpy, screen, 32, TrueColor, &vinfo)) {
fprintf(stderr, "No 32-bit TrueColor visual available\n");
return 1;
}
// Setup window attributes
XSetWindowAttributes attrs;
attrs.colormap = XCreateColormap(dpy, root, vinfo.visual, AllocNone);
attrs.background_pixel = 0x00000000;
attrs.border_pixel = 0;
attrs.override_redirect = True;
const int win_w = 800;
const int win_h = 800;
int screen_height = DisplayHeight(dpy, screen);
int screen_width = DisplayWidth(dpy, screen);
Window win = XCreateWindow(
dpy, root,
screen_width-win_w, screen_height-win_h, win_w, win_h,
0, vinfo.depth, InputOutput, vinfo.visual,
CWColormap | CWBackPixel | CWBorderPixel | CWOverrideRedirect,
&attrs
);
XMapWindow(dpy, win);
// Create XRender Picture for drawing
XRenderPictFormat* fmt = XRenderFindVisualFormat(dpy, vinfo.visual);
Picture pict = XRenderCreatePicture(dpy, win, fmt, 0, NULL);
//XRenderColor background = {0, 0, 0, 0}; // transparant
XRenderColor background = {0, 0xffff, 0, 0xffff};
//XRenderColor red = {0xffff, 0x0000, 0x0000, 0xffff}; // solid red
///////////////////////////////
// load image
int img_w, img_h, img_channels;
unsigned char* data = stbi_load("tst.png", &img_w, &img_h, &img_channels, 4);
if (!data) {
fprintf(stderr, "Failed to load image\n");
return 1;
}
// Create Pixmap + XImage
Pixmap img_pixmap = XCreatePixmap(dpy, win, img_w, img_h, vinfo.depth);
XImage* ximage = XCreateImage(
dpy, vinfo.visual, vinfo.depth, ZPixmap, 0,
(char*)data, img_w, img_h, 32, 0
);
// Put image into pixmap
GC gc = XCreateGC(dpy, img_pixmap, 0, NULL);
XPutImage(dpy, img_pixmap, gc, ximage, 0, 0, 0, 0, img_w, img_h);
// Create Picture from pixmap
Picture img_picture = XRenderCreatePicture(dpy, img_pixmap, fmt, 0, NULL);
////////////////////////////////////
while (1) {
// Clear window to transparent
XRectangle clear_rect = {0, 0, win_w, win_h};
XRenderFillRectangles(dpy, PictOpSrc, pict, &background, &clear_rect, 1);
// Draw image
XRenderComposite(dpy, PictOpOver, img_picture, None, pict,
0, 0, 0, 0,
0, 0, 300, 300); // dest x,y + size
XFlush(dpy);
usleep(16000); // ~60 FPS
}
// Cleanup (never actually reached)
XRenderFreePicture(dpy, pict);
XCloseDisplay(dpy);
return 0;
}
Detect global X11 input
-lX11 -lXtst
#include <stdio.h>
#include <X11/XKBlib.h>
#include <X11/extensions/record.h>
void key_pressed_cb(XPointer arg, XRecordInterceptData *d) {
if (d->category != XRecordFromServer)
return;
int key = ((unsigned char*) d->data)[1];
int type = ((unsigned char*) d->data)[0] & 0x7F;
int repeat = d->data[2] & 1;
if(!repeat) {
switch (type) {
case KeyPress:
printf("key press %d\n", key);
break;
case KeyRelease:
printf("key release %d\n", key);
break;
case ButtonPress:
printf("button press %d\n", key);
break;
case ButtonRelease:
printf("button release %d\n", key);
break;
default:
break;
}
}
XRecordFreeData (d);
}
void scan(int verbose) {
XRecordRange* rr;
XRecordClientSpec rcs;
XRecordContext rc;
Display *dpy = XOpenDisplay(NULL);
rr = XRecordAllocRange();
rr->device_events.first = KeyPress;
rr->device_events.last = ButtonReleaseMask;
rcs = XRecordAllClients;
rc = XRecordCreateContext (dpy, 0, &rcs, 1, &rr, 1);
XFree (rr);
XRecordEnableContext(dpy, rc, key_pressed_cb, NULL);
}
int main() {
scan(True);
return 0;
}
Detect global input asynchronously (kinda hacky…)
#include <stdio.h>
#include <unistd.h>
#include <X11/XKBlib.h>
#include <X11/extensions/record.h>
#include <sys/mman.h>
int *any_key_pressed;
void key_pressed_cb(XPointer arg, XRecordInterceptData *d) {
if (d->category != XRecordFromServer)
return;
int key = ((unsigned char*) d->data)[1];
int type = ((unsigned char*) d->data)[0] & 0x7F;
int repeat = d->data[2] & 1;
if(!repeat) {
switch (type) {
case KeyPress:
*any_key_pressed = 1;
//printf("key press %d\n", key);
break;
case KeyRelease:
//printf("key release %d\n", key);
break;
case ButtonPress:
//printf("button press %d\n", key);
break;
case ButtonRelease:
//printf("button release %d\n", key);
break;
default:
break;
}
}
XRecordFreeData (d);
}
void scan() {
XRecordRange* rr;
XRecordClientSpec rcs;
XRecordContext rc;
Display *dpy = XOpenDisplay(NULL);
rr = XRecordAllocRange();
rr->device_events.first = KeyPress;
rr->device_events.last = ButtonReleaseMask;
rcs = XRecordAllClients;
rc = XRecordCreateContext (dpy, 0, &rcs, 1, &rr, 1);
XFree (rr);
XRecordEnableContext(dpy, rc, key_pressed_cb, NULL);
}
int main() {
any_key_pressed = mmap(NULL, sizeof(int),
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS,
-1, 0);
if (fork()) {
scan();
} else {
while (1) {
if (*any_key_pressed == 1) {
printf("pressed\n");
*any_key_pressed = 0;
}
}
}
return 0;
}
Detect on both wayland/xorg:
evtest /dev/input/by-id/usb-*event-kbd
https://github.com/freedesktop-unofficial-mirror/evtest/blob/master/evtest.c
#include <stdio.h>
#include <linux/input.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
void capture(char* filename)
{
int fd = open(filename, O_RDONLY);
struct input_event ev[64];
int i, rd;
while (1) {
rd = read(fd, ev, sizeof(ev));
for (i = 0; i < rd / sizeof(struct input_event); i++) {
printf("a key was pressed!\n");
fflush(stdout);
}
}
}
int main () {
FILE *fp;
fp = popen("/bin/echo /dev/input/by-path/*event-kbd", "r");
char devices[1000];
fgets(devices, sizeof(devices), fp);
devices[strlen(devices)-1] = '\0'; // just remove newline
pclose(fp);
char *device, *str;
str = strdup(devices);
while ((device = strsep(&str, " "))) {
if (!fork()) {
capture(device);
}
}
}
Wayland window:
https://www.youtube.com/watch?v=iIVIu7YRdY0
https://github.com/willth7/wayland-client-example
#include <wayland-client.h>
#include "xdg-shell-client-protocol.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
struct wl_compositor* comp;
struct wl_surface* srfc;
struct wl_buffer* bfr;
struct wl_shm* shm;
struct xdg_wm_base* sh;
struct xdg_toplevel* top;
struct wl_seat* seat;
struct wl_keyboard* kb;
uint8_t* pixl;
uint16_t w = 200;
uint16_t h = 100;
uint8_t c;
uint8_t cls;
int32_t alc_shm(uint64_t sz) {
char name[8];
name[0] = '/';
name[7] = 0;
for (uint8_t i = 1; i < 6; i++) {
name[i] = (rand() & 23) + 97;
}
int32_t fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR | S_IWOTH | S_IROTH);
shm_unlink(name);
ftruncate(fd, sz);
return fd;
}
void resz() {
int32_t fd = alc_shm(w * h * 4);
pixl = mmap(0, w * h * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
struct wl_shm_pool* pool = wl_shm_create_pool(shm, fd, w * h * 4);
bfr = wl_shm_pool_create_buffer(pool, 0, w, h, w * 4, WL_SHM_FORMAT_ARGB8888);
wl_shm_pool_destroy(pool);
close(fd);
}
void draw() {
memset(pixl, c, w * h * 4);
wl_surface_attach(srfc, bfr, 0, 0);
wl_surface_damage_buffer(srfc, 0, 0, w, h);
wl_surface_commit(srfc);
}
struct wl_callback_listener cb_list;
void frame_new(void* data, struct wl_callback* cb, uint32_t a) {
wl_callback_destroy(cb);
cb = wl_surface_frame(srfc);
wl_callback_add_listener(cb, &cb_list, 0);
c++;
draw();
}
struct wl_callback_listener cb_list = {
.done = frame_new
};
void xrfc_conf(void* data, struct xdg_surface* xrfc, uint32_t ser) {
xdg_surface_ack_configure(xrfc, ser);
if (!pixl) {
resz();
}
draw();
}
struct xdg_surface_listener xrfc_list = {
.configure = xrfc_conf
};
void top_conf(void* data, struct xdg_toplevel* top, int32_t nw, int32_t nh, struct wl_array* stat) {
if (!nw && !nh) {
return;
}
if (w != nw || h != nh) {
munmap(pixl, w * h * 4);
w = nw;
h = nh;
resz();
}
}
void top_cls(void* data, struct xdg_toplevel* top) {
cls = 1;
}
struct xdg_toplevel_listener top_list = {
.configure = top_conf,
.close = top_cls
};
void sh_ping(void* data, struct xdg_wm_base* sh, uint32_t ser) {
xdg_wm_base_pong(sh, ser);
}
struct xdg_wm_base_listener sh_list = {
.ping = sh_ping
};
void kb_map(void* data, struct wl_keyboard* kb, uint32_t frmt, int32_t fd, uint32_t sz) {
}
void kb_enter(void* data, struct wl_keyboard* kb, uint32_t ser, struct wl_surface* srfc, struct wl_array* keys) {
}
void kb_leave(void* data, struct wl_keyboard* kb, uint32_t ser, struct wl_surface* srfc) {
}
void kb_key(void* data, struct wl_keyboard* kb, uint32_t ser, uint32_t t, uint32_t key, uint32_t stat) {
if (key == 1) {
cls = 1;
}
else if (key == 30) {
printf("a\n");
}
else if (key == 32) {
printf("d\n");
}
}
void kb_mod(void* data, struct wl_keyboard* kb, uint32_t ser, uint32_t dep, uint32_t lat, uint32_t lock, uint32_t grp) {
}
void kb_rep(void* data, struct wl_keyboard* kb, int32_t rate, int32_t del) {
}
struct wl_keyboard_listener kb_list = {
.keymap = kb_map,
.enter = kb_enter,
.leave = kb_leave,
.key = kb_key,
.modifiers = kb_mod,
.repeat_info = kb_rep
};
void seat_cap(void* data, struct wl_seat* seat, uint32_t cap) {
if (cap & WL_SEAT_CAPABILITY_KEYBOARD && !kb) {
kb = wl_seat_get_keyboard(seat);
wl_keyboard_add_listener(kb, &kb_list, 0);
}
}
void seat_name(void* data, struct wl_seat* seat, const char* name) {
}
struct wl_seat_listener seat_list = {
.capabilities = seat_cap,
.name = seat_name
};
void reg_glob(void* data, struct wl_registry* reg, uint32_t name, const char* intf, uint32_t v) {
if (!strcmp(intf, wl_compositor_interface.name)) {
comp = wl_registry_bind(reg, name, &wl_compositor_interface, 4);
}
else if (!strcmp(intf, wl_shm_interface.name)) {
shm = wl_registry_bind(reg, name, &wl_shm_interface, 1);
}
else if (!strcmp(intf, xdg_wm_base_interface.name)) {
sh = wl_registry_bind(reg, name, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(sh, &sh_list, 0);
}
else if (!strcmp(intf, wl_seat_interface.name)) {
seat = wl_registry_bind(reg, name, &wl_seat_interface, 1);
wl_seat_add_listener(seat, &seat_list, 0);
}
}
void reg_glob_rem(void* data, struct wl_registry* reg, uint32_t name) {
}
struct wl_registry_listener reg_list = {
.global = reg_glob,
.global_remove = reg_glob_rem
};
int main() {
struct wl_display* disp = wl_display_connect(0);
struct wl_registry* reg = wl_display_get_registry(disp);
wl_registry_add_listener(reg, ®_list, 0);
wl_display_roundtrip(disp);
srfc = wl_compositor_create_surface(comp);
struct wl_callback* cb = wl_surface_frame(srfc);
wl_callback_add_listener(cb, &cb_list, 0);
struct xdg_surface* xrfc = xdg_wm_base_get_xdg_surface(sh, srfc);
xdg_surface_add_listener(xrfc, &xrfc_list, 0);
top = xdg_surface_get_toplevel(xrfc);
xdg_toplevel_add_listener(top, &top_list, 0);
xdg_toplevel_set_title(top, "wayland client");
wl_surface_commit(srfc);
while (wl_display_dispatch(disp)) {
if (cls) break;
}
if (kb) {
wl_keyboard_destroy(kb);
}
wl_seat_release(seat);
if (bfr) {
wl_buffer_destroy(bfr);
}
xdg_toplevel_destroy(top);
xdg_surface_destroy(xrfc);
wl_surface_destroy(srfc);
wl_display_disconnect(disp);
return 0;
}
[~/t]
$ wayland-scanner client-header \
/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml \
xdg-shell-client-protocol.h
wayland-scanner private-code \
/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml \
xdg-shell-protocol.c
[~/t]
$ l
a.c xdg-shell-client-protocol.h xdg-shell-protocol.c
[~/t]
$ gcc xdg-shell-protocol.c a.c -lwayland-client
Wayland window unaffected by tiling:
#define _POSIX_C_SOURCE 200809L
#include <wayland-client.h>
#include "zwlr-layer-shell-v1-client-protocol.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
struct wl_display* display;
struct wl_compositor* compositor;
struct wl_shm* shm;
struct zwlr_layer_shell_v1* layer_shell;
struct wl_surface* surface;
struct wl_buffer* buffer;
struct zwlr_layer_surface_v1* layer_surface;
uint8_t* pixels;
int width = 1920;
int height = 30;
#include <errno.h>
int create_shm(int size) {
char name[] = "/my-shmXXXXXX";
int retries = 100;
int fd = -1;
for (int i = 0; i < retries; ++i) {
for (int j = 0; j < 6; ++j)
name[8 + j] = 'A' + (rand() % 26);
fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd >= 0) {
shm_unlink(name);
break;
} else if (errno != EEXIST) {
break;
}
}
if (fd >= 0)
ftruncate(fd, size);
return fd;
}
void draw_bar() {
memset(pixels, 0x33, width * height * 4); // Dark gray
wl_surface_attach(surface, buffer, 0, 0);
wl_surface_damage_buffer(surface, 0, 0, width, height);
wl_surface_commit(surface);
}
void layer_surface_configure(void* data, struct zwlr_layer_surface_v1* ls, uint32_t serial, uint32_t w, uint32_t h) {
zwlr_layer_surface_v1_ack_configure(ls, serial);
draw_bar();
}
struct zwlr_layer_surface_v1_listener layer_listener = {
.configure = layer_surface_configure,
.closed = NULL
};
void shm_format(void* data, struct wl_shm* shm, uint32_t fmt) {}
struct wl_shm_listener shm_listener = { .format = shm_format };
void registry_global(void* data, struct wl_registry* reg, uint32_t name, const char* interface, uint32_t ver) {
if (strcmp(interface, wl_compositor_interface.name) == 0) {
compositor = wl_registry_bind(reg, name, &wl_compositor_interface, 4);
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
shm = wl_registry_bind(reg, name, &wl_shm_interface, 1);
wl_shm_add_listener(shm, &shm_listener, NULL);
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
layer_shell = wl_registry_bind(reg, name, &zwlr_layer_shell_v1_interface, 1);
}
}
void registry_remove(void* data, struct wl_registry* reg, uint32_t name) {}
struct wl_registry_listener reg_listener = {
.global = registry_global,
.global_remove = registry_remove
};
int main() {
display = wl_display_connect(NULL);
struct wl_registry* registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, ®_listener, NULL);
wl_display_roundtrip(display);
surface = wl_compositor_create_surface(compositor);
layer_surface = zwlr_layer_shell_v1_get_layer_surface(
layer_shell, surface, NULL,
ZWLR_LAYER_SHELL_V1_LAYER_TOP,
"bar"
);
zwlr_layer_surface_v1_set_anchor(layer_surface,
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
zwlr_layer_surface_v1_set_size(layer_surface, 0, height);
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, height);
zwlr_layer_surface_v1_add_listener(layer_surface, &layer_listener, NULL);
wl_surface_commit(surface);
int size = width * height * 4;
int fd = create_shm(size);
pixels = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
struct wl_shm_pool* pool = wl_shm_create_pool(shm, fd, size);
buffer = wl_shm_pool_create_buffer(pool, 0, width, height, width * 4, WL_SHM_FORMAT_ARGB8888);
wl_shm_pool_destroy(pool);
close(fd);
while (wl_display_dispatch(display) != -1) {}
return 0;
}
gcc a.c zwlr-layer-shell-v1-protocol.c xdg-shell-protocol.c -lwayland-client
using GTK
GTK is a c library for GUI, more high level than direct X11 or wayland
https://docs.gtk.org/gtk4/getting_started.html
#include <gtk/gtk.h>
static void on_activate (GtkApplication *app) {
// Create a new window
GtkWidget *window = gtk_application_window_new(app);
gtk_window_present (GTK_WINDOW (window));
}
int main (int argc, char *argv[]) {
// Create a new application
GtkApplication *app = gtk_application_new("com.example.GtkApplication", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect(app, "activate", G_CALLBACK (on_activate), NULL);
return g_application_run(G_APPLICATION (app), argc, argv);
}
Compile with
gcc $( pkg-config --cflags gtk4 ) -o example-0 example-0.c $( pkg-config --libs gtk4 )
But the clangd couldn’t find the GTK library
https://www.reddit.com/r/cprogramming/comments/16d39ht/how_can_i_setup_my_lsp_to_work_with_gtk4/
TLDR you just need compile_commands.json in your project directory, which can be auto-created with bear
example Makefile:
CC = gcc
CFLAGS = $(shell pkg-config --cflags gtk4)
LDFLAGS = $(shell pkg-config --libs gtk4)
install:
$(CC) main.c -o chess $(CFLAGS) $(LDFLAGS)
clean:
-rm chess
Then run bear -- make
, you only have to run that to create compile_commands.json, then you can go back to using regular make
I found this cool simple minesweeper clone https://github.com/MelonFruit7/MinesweeperRemake/