X11+SDL2 -> xcb+cairo

This commit is contained in:
Casey 2024-04-09 17:29:48 +03:00
parent 0043dc5ef5
commit 6aa7fb12b4
Signed by: hkc
GPG Key ID: F0F6CFE11CDB0960
8 changed files with 158 additions and 172 deletions

View File

@ -1,6 +1,6 @@
CFLAGS += CFLAGS +=
LDFLAGS := -lm -lX11 -lSDL2 -lSDL2_image LDFLAGS := -lm -lcairo -lxcb
OBJECTS := obj/rootwindow.o obj/sdl_xroot.o OBJECTS := obj/rootwindow.o obj/cairo_context.o
livewp: lib livewp: lib
$(CC) $(CFLAGS) $(OBJECTS) src/main.c $(LDFLAGS) -o livewp $(CC) $(CFLAGS) $(OBJECTS) src/main.c $(LDFLAGS) -o livewp

47
src/cairo_context.c Normal file
View File

@ -0,0 +1,47 @@
#include "cairo_context.h"
#include "rootwindow.h"
#include <cairo/cairo-xcb.h>
#include <stdio.h>
#include <xcb/xproto.h>
bool cairo_ctx_create(const char *display, struct cairo_ctx *ctx) {
if (!(ctx->connection = xcb_connect(display, NULL))) return false;
// grab first screen
ctx->screen = xcb_setup_roots_iterator(xcb_get_setup(ctx->connection)).data;
for (
xcb_depth_iterator_t iter_depth = xcb_screen_allowed_depths_iterator(ctx->screen);
iter_depth.rem;
xcb_depth_next(&iter_depth)) {
int visual_length = xcb_depth_visuals_length(iter_depth.data);
xcb_visualtype_t *visual = xcb_depth_visuals(iter_depth.data);
for (int i = 0; i < visual_length; i++) {
if (visual->visual_id == ctx->screen->root_visual) {
ctx->visual = visual;
break;
}
}
}
ctx->wid = create_root_window(ctx->connection, ctx->screen);
ctx->surface = cairo_xcb_surface_create(ctx->connection, ctx->wid, ctx->visual, ctx->screen->width_in_pixels, ctx->screen->height_in_pixels);
ctx->cairo = cairo_create(ctx->surface);
return true;
}
void cairo_begin(struct cairo_ctx *ctx) {
xcb_clear_area(ctx->connection, 0, ctx->wid, 0, 0, 0, 0);
}
void cairo_flush(struct cairo_ctx *ctx) {
xcb_flush(ctx->connection);
}
void cairo_ctx_close(struct cairo_ctx *ctx) {
cairo_destroy(ctx->cairo);
xcb_destroy_window(ctx->connection, ctx->wid);
xcb_disconnect(ctx->connection);
}

22
src/cairo_context.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef _CAIRO_CONTEXT_H_
#define _CAIRO_CONTEXT_H_
#include <cairo/cairo.h>
#include <xcb/xcb.h>
#include <stdbool.h>
struct cairo_ctx {
cairo_t *cairo;
cairo_surface_t *surface;
xcb_connection_t *connection;
xcb_screen_t *screen;
xcb_window_t wid;
xcb_visualtype_t *visual;
};
bool cairo_ctx_create(const char *display, struct cairo_ctx *ctx);
void cairo_begin(struct cairo_ctx *ctx);
void cairo_flush(struct cairo_ctx *ctx);
void cairo_ctx_close(struct cairo_ctx *ctx);
#endif

View File

@ -1,20 +1,17 @@
#include <SDL2/SDL.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include "sdl_xroot.h" #include "cairo_context.h"
struct sdlxroot_context ctx; struct cairo_ctx ctx;
SDL_Renderer *renderer;
volatile bool running = true; volatile bool running = true;
void cleanup(void) { void cleanup(void) {
SDL_DestroyRenderer(renderer); cairo_ctx_close(&ctx);
sdlxroot_close(&ctx);
} }
void sighandler(int sig) { void sighandler(int sig) {
@ -22,7 +19,7 @@ void sighandler(int sig) {
} }
int main(void) { int main(void) {
if (!sdlxroot_create(NULL, &ctx)) { if (!cairo_ctx_create(NULL, &ctx)) {
fprintf(stderr, "Failed creating window or something\n"); fprintf(stderr, "Failed creating window or something\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -30,14 +27,13 @@ int main(void) {
atexit(cleanup); atexit(cleanup);
signal(SIGINT, sighandler); signal(SIGINT, sighandler);
renderer = SDL_CreateRenderer(ctx.window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); for (float t = 0.0; running; t += 1.0 / 60.0) {
cairo_begin(&ctx);
while (running) { {
SDL_SetRenderDrawColor(renderer, 0x13, 0x13, 0x13, 0); cairo_set_source_rgb(ctx.cairo, 0.0, 0.5 - sin(t) * 0.5, 0.5 - cos(t) * 0.5);
SDL_RenderClear(renderer); cairo_paint(ctx.cairo);
SDL_SetRenderDrawColor(renderer, 0xff, 0x00, 0x00, 0xff); }
SDL_RenderDrawLine(renderer, 200, 200, 600, 600); cairo_flush(&ctx);
SDL_RenderPresent(renderer);
usleep(1000); usleep(1000);
} }

View File

@ -1,119 +1,97 @@
#include "rootwindow.h" #include "rootwindow.h"
#include <X11/X.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <errno.h> #include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <xcb/xcb.h>
#define ATOM(a) XInternAtom(display, #a, False) xcb_window_t find_subwindow(xcb_connection_t *connection, xcb_window_t win, int w, int h);
Window find_desktop_window(Display *display, int screen, Window *ptr_root, Window *ptr_desktop);
Window find_subwindow(Display *display, Window win, int w, int h, int dw, int dh);
Window create_root_window(Display *display, int screen) { xcb_window_t create_root_window(xcb_connection_t *connection, xcb_screen_t *screen) {
// below, sticky, fullscreen, skip_taskbar, skip_pager, noFocus, override, xcb_window_t root = find_subwindow(connection, screen->root, -1, -1);
// set_desktop_type if (!root) return 0;
XSetWindowAttributes attrs = { xcb_window_t parent = find_subwindow(connection, root, screen->width_in_pixels, screen->height_in_pixels);
ParentRelative, 0L, 0, 0L, 0, 0, Always, 0L, 0L, False, StructureNotifyMask | ExposureMask, 0L, True, 0, 0 if (!parent) return 0;
xcb_window_t wid = xcb_generate_id(connection);
{
uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_BACKING_STORE | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
uint32_t values[] = {
screen->white_pixel,
XCB_BACKING_STORE_ALWAYS,
1,
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_FOCUS_CHANGE
}; };
int screen_w = DisplayWidth(display, screen), xcb_create_window(
screen_h = DisplayHeight(display, screen); connection,
XCB_COPY_FROM_PARENT,
Window root, desktop; wid,
if (!find_desktop_window(display, screen, &root, &desktop)) { parent,
fprintf(stderr, "find_desktop_window() failed\n"); 0, 0, screen->width_in_pixels, screen->height_in_pixels, 0,
return 0; XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
mask, values);
} }
Window window = XCreateWindow(display, desktop, 0, 0, screen_w, screen_h, 0, CopyFromParent, InputOutput, CopyFromParent, CWOverrideRedirect | CWBackingStore, &attrs); {
if (!window) { const static uint32_t values[] = { XCB_STACK_MODE_BELOW };
fprintf(stderr, "XCreateWindow() failed: %s\n", strerror(errno)); xcb_configure_window(connection, wid, XCB_CONFIG_WINDOW_STACK_MODE, values);
}
XLowerWindow(display, window);
XMapWindow(display, window);
XSync(display, window);
return window;
} }
// from xwinwrap xcb_map_window(connection, wid);
Window find_subwindow(Display *display, Window win, int w, int h, int dw, int dh) { xcb_flush(connection);
unsigned int i, j;
Window troot, parent, *children;
unsigned int n;
/* search subwindows with same size as display or work area */ return wid;
for (i = 0; i < 10; i++) {
XQueryTree(display, win, &troot, &parent, &children, &n);
for (j = 0; j < n; j++) {
XWindowAttributes attrs;
if (XGetWindowAttributes(display, children[j], &attrs)) {
/* Window must be mapped and same size as display or
* work space */
if (attrs.map_state != 0 &&
((attrs.width == dw && attrs.height == dh) ||
(attrs.width == w && attrs.height == h))) {
win = children[j];
break;
}
}
} }
XFree(children);
if (j == n) { bool get_window_attributes(xcb_connection_t *connection, xcb_window_t wid, xcb_get_window_attributes_reply_t *out) {
break; xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(connection, wid);
} xcb_get_window_attributes_reply_t *reply = xcb_get_window_attributes_reply(connection, cookie, 0);
if (!reply) return false;
memcpy(out, reply, sizeof(xcb_get_window_attributes_reply_t));
free(reply);
return true;
} }
bool get_window_geometry(xcb_connection_t *connection, xcb_window_t wid, xcb_rectangle_t *rect) {
xcb_get_geometry_cookie_t cookie = xcb_get_geometry(connection, wid);
xcb_get_geometry_reply_t *reply = xcb_get_geometry_reply(connection, cookie, 0);
if (!reply) return false;
rect->x = reply->x;
rect->y = reply->y;
rect->width = reply->width;
rect->height = reply->height;
free(reply);
return true;
}
xcb_window_t find_subwindow(xcb_connection_t *connection, xcb_window_t win, int w, int h) {
xcb_query_tree_reply_t *qt_reply;
xcb_query_tree_cookie_t qt_cookie = xcb_query_tree(connection, win);
if ((qt_reply = xcb_query_tree_reply(connection, qt_cookie, 0))) {
xcb_window_t *children = xcb_query_tree_children(qt_reply);
xcb_rectangle_t rect;
xcb_get_window_attributes_reply_t attrs;
for (int i = 0; i < xcb_query_tree_children_length(qt_reply); i++) {
if (!get_window_geometry(connection, children[i], &rect)) continue;
if (!get_window_attributes(connection, children[i], &attrs)) continue;
if (attrs.map_state != 0 && rect.width == w && rect.height == h) {
free(qt_reply);
return children[i];
}
}
free(qt_reply);
}
return win; return win;
} }
Window find_desktop_window(Display *display, int screen, Window *ptr_root, Window *ptr_desktop) {
int display_width, display_height;
Atom type;
int format;
unsigned long n_items, n_bytes;
unsigned char *buf = NULL;
Window root = RootWindow(display, screen);
Window win = root;
Window tmp_root, parent, *children;
unsigned int n_windows;
XQueryTree(display, root, &tmp_root, &parent, &children, &n_windows);
for (int i = 0; i < (int)n_windows; i++) {
if (XGetWindowProperty(display, children[i], ATOM(__SWM_VROOT), 0, 1, False, XA_WINDOW, &type, &format, &n_items, &n_bytes, &buf) == Success && type == XA_WINDOW) {
win = *(Window *)buf;
XFree(buf);
XFree(children);
*ptr_root = win;
*ptr_desktop = win;
return win;
}
if (buf) {
XFree(buf);
buf = NULL;
}
}
XFree(children);
win = find_subwindow(display, root, -1, -1, display_width, display_height);
display_width = DisplayWidth(display, screen);
display_height = DisplayHeight(display, screen);
win = find_subwindow(display, win, display_width, display_height, display_width, display_height);
if (buf) {
XFree(buf);
buf = NULL;
}
*ptr_root = root;
*ptr_desktop = win;
return win;
}

View File

@ -1,8 +1,8 @@
#ifndef _ROOTWINDOW_H_ #ifndef _ROOTWINDOW_H_
#define _ROOTWINDOW_H_ #define _ROOTWINDOW_H_
#include <X11/Xlib.h> #include <xcb/xcb.h>
Window create_root_window(Display *display, int screen); xcb_window_t create_root_window(xcb_connection_t *connection, xcb_screen_t *screen);
#endif #endif

View File

@ -1,40 +0,0 @@
#include "sdl_xroot.h"
#include "rootwindow.h"
#include <SDL2/SDL.h>
#include <X11/X.h>
#include <stdio.h>
bool sdlxroot_create(const char *display_name, struct sdlxroot_context *ctx) {
Display *display = XOpenDisplay(display_name);
if (!display) return false;
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
// TODO: error handling
// FIXME: what is the invalid value for `Window`? -1? 0? some other value?
// docs aren't clear about it, or i'm just blind
ctx->x11_screen = DefaultScreen(display);
Window root_window = create_root_window(display, ctx->x11_screen);
if (!root_window) {
XCloseDisplay(display);
fprintf(stderr, "create_root_window() failed\n");
return false;
}
ctx->x11_display = display;
ctx->x11_window = root_window;
ctx->window = SDL_CreateWindowFrom((void *)ctx->x11_window);
if (!ctx->window) {
fprintf(stderr, "SDL_CreateWindowFrom(0x%08lx) failed\n", ctx->x11_window);
}
return ctx->window != NULL;
}
void sdlxroot_close(struct sdlxroot_context *ctx) {
SDL_DestroyWindow(ctx->window);
XDestroyWindow((Display *)ctx->x11_display, (Window)ctx->x11_window);
XCloseDisplay((Display *)ctx->x11_display);
}

View File

@ -1,17 +0,0 @@
#ifndef _SDL_XROOT_H_
#define _SDL_XROOT_H_
#include <SDL2/SDL.h>
#include <stdbool.h>
struct sdlxroot_context {
SDL_Window *window;
void *x11_display;
unsigned long x11_window;
int x11_screen;
};
bool sdlxroot_create(const char *display_name, struct sdlxroot_context *ctx);
void sdlxroot_close(struct sdlxroot_context *ctx);
#endif