X11+SDL2 -> xcb+cairo
This commit is contained in:
parent
0043dc5ef5
commit
6aa7fb12b4
4
Makefile
4
Makefile
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
26
src/main.c
26
src/main.c
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
176
src/rootwindow.c
176
src/rootwindow.c
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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
|
|
Loading…
Reference in New Issue