From bfcabe48ef3fc7a0388de007504fc232f826fb84 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 12 Nov 2015 19:04:01 -0500 Subject: [PATCH] Start fleshing out wayland client implementation This introduces a basic shared framework for making wayland clients within sway itself. --- CMakeLists.txt | 4 +- {sway => common}/list.c | 0 {sway => common}/log.c | 88 ------------------ include/client.h | 43 +++++++++ sway/debug_log.c | 102 +++++++++++++++++++++ swaybg/CMakeLists.txt | 10 ++- swaybg/main.c | 23 ++++- wayland/client.c | 192 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 370 insertions(+), 92 deletions(-) rename {sway => common}/list.c (100%) rename {sway => common}/log.c (56%) create mode 100644 include/client.h create mode 100644 sway/debug_log.c create mode 100644 wayland/client.c diff --git a/CMakeLists.txt b/CMakeLists.txt index d446c2b38..b75e87378 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ set(CMAKE_C_FLAGS "-g") set(CMAKE_C_STANDARD 99) SET(CMAKE_C_EXTENSIONS OFF) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "bin/") -add_definitions("-Wall -Wextra -Wno-unused-parameter") +add_definitions("-Wall -Wextra -Wno-unused-parameter -D_GNU_SOURCE") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMake) add_subdirectory(swaybg swaybg) @@ -44,6 +44,7 @@ find_package(PCRE REQUIRED) find_package(JsonC REQUIRED) FILE(GLOB sources ${PROJECT_SOURCE_DIR}/sway/*.c) +FILE(GLOB common ${PROJECT_SOURCE_DIR}/common/*.c) include_directories( ${WLC_INCLUDE_DIRS} @@ -55,6 +56,7 @@ include_directories( add_executable(sway ${sources} + ${common} ) target_link_libraries(sway diff --git a/sway/list.c b/common/list.c similarity index 100% rename from sway/list.c rename to common/list.c diff --git a/sway/log.c b/common/log.c similarity index 56% rename from sway/log.c rename to common/log.c index f2b828f93..02aac4c1a 100644 --- a/sway/log.c +++ b/common/log.c @@ -157,91 +157,3 @@ void error_handler(int sig) { } exit(1); } - -#include "workspace.h" - -/* XXX:DEBUG:XXX */ -static void container_log(const swayc_t *c) { - fprintf(stderr, "focus:%c|", - c == get_focused_view(&root_container) ? 'K': - c == get_focused_container(&root_container) ? 'F' : // Focused - c == swayc_active_workspace() ? 'W' : // active workspace - c == &root_container ? 'R' : // root - 'X');// not any others - fprintf(stderr,"(%p)",c); - fprintf(stderr,"(p:%p)",c->parent); - fprintf(stderr,"(f:%p)",c->focused); - fprintf(stderr,"(h:%ld)",c->handle); - fprintf(stderr,"Type:"); - fprintf(stderr, - c->type == C_ROOT ? "Root|" : - c->type == C_OUTPUT ? "Output|" : - c->type == C_WORKSPACE ? "Workspace|" : - c->type == C_CONTAINER ? "Container|" : - c->type == C_VIEW ? "View|" : "Unknown|"); - fprintf(stderr,"layout:"); - fprintf(stderr, - c->layout == L_NONE ? "NONE|" : - c->layout == L_HORIZ ? "Horiz|": - c->layout == L_VERT ? "Vert|": - c->layout == L_STACKED ? "Stacked|": - c->layout == L_FLOATING ? "Floating|": - "Unknown|"); - fprintf(stderr, "w:%.f|h:%.f|", c->width, c->height); - fprintf(stderr, "x:%.f|y:%.f|", c->x, c->y); - fprintf(stderr, "g:%d|",c->gaps); - fprintf(stderr, "vis:%c|", c->visible?'t':'f'); - fprintf(stderr, "name:%.16s|", c->name); - fprintf(stderr, "children:%d\n",c->children?c->children->length:0); -} -void layout_log(const swayc_t *c, int depth) { - if (L_DEBUG > v) return; - int i, d; - int e = c->children ? c->children->length : 0; - container_log(c); - if (e) { - for (i = 0; i < e; ++i) { - fputc('|',stderr); - for (d = 0; d < depth; ++d) fputc('-', stderr); - layout_log(c->children->items[i], depth + 1); - } - } - if (c->type == C_WORKSPACE) { - e = c->floating?c->floating->length:0; - if (e) { - for (i = 0; i < e; ++i) { - fputc('|',stderr); - for (d = 0; d < depth; ++d) fputc('=', stderr); - layout_log(c->floating->items[i], depth + 1); - } - } - } -} - -const char *swayc_type_string(enum swayc_types type) { - return type == C_ROOT ? "ROOT" : - type == C_OUTPUT ? "OUTPUT" : - type == C_WORKSPACE ? "WORKSPACE" : - type == C_CONTAINER ? "CONTAINER" : - type == C_VIEW ? "VIEW" : - "UNKNOWN"; -} - -// Like sway_log, but also appends some info about given container to log output. -void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ...) { - sway_assert(cont, "swayc_log: no container ..."); - va_list args; - va_start(args, format); - char *txt = malloc(128); - vsprintf(txt, format, args); - va_end(args); - - char *debug_txt = malloc(32); - snprintf(debug_txt, 32, "%s '%s'", swayc_type_string(cont->type), cont->name); - - sway_log(verbosity, "%s (%s)", txt, debug_txt); - free(txt); - free(debug_txt); -} - -/* XXX:DEBUG:XXX */ diff --git a/include/client.h b/include/client.h new file mode 100644 index 000000000..ec3537ca7 --- /dev/null +++ b/include/client.h @@ -0,0 +1,43 @@ +#ifndef _CLIENT_H +#define _CLIENT_H + +#include +#include +#include +#include "list.h" + +struct output_state { + struct wl_output *output; + uint32_t flags; + int width, height; +}; + +struct buffer { + struct wl_buffer *buffer; + struct wl_shm_pool *pool; + uint32_t width, height; +}; + +struct client_state { + struct wl_compositor *compositor; + struct wl_display *display; + struct wl_pointer *pointer; + struct wl_seat *seat; + struct wl_shell *shell; + struct wl_shm *shm; + struct buffer *buffer; + struct wl_surface *surface; + struct wl_shell_surface *shell_surface; + cairo_t *cairo; + cairo_surface_t *cairo_surface; + PangoContext *pango; + list_t *outputs; +}; + +struct client_state *client_setup(void); +void client_teardown(struct client_state *state); +struct buffer *create_memory_pool(struct client_state *state, int32_t width, int32_t height, uint32_t format); +int client_prerender(struct client_state *state); +int client_render(struct client_state *state); + +#endif diff --git a/sway/debug_log.c b/sway/debug_log.c new file mode 100644 index 000000000..4cb975618 --- /dev/null +++ b/sway/debug_log.c @@ -0,0 +1,102 @@ +#include "log.h" +#include "sway.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "workspace.h" + +extern log_importance_t v; + +/* XXX:DEBUG:XXX */ +static void container_log(const swayc_t *c) { + fprintf(stderr, "focus:%c|", + c == get_focused_view(&root_container) ? 'K': + c == get_focused_container(&root_container) ? 'F' : // Focused + c == swayc_active_workspace() ? 'W' : // active workspace + c == &root_container ? 'R' : // root + 'X');// not any others + fprintf(stderr,"(%p)",c); + fprintf(stderr,"(p:%p)",c->parent); + fprintf(stderr,"(f:%p)",c->focused); + fprintf(stderr,"(h:%ld)",c->handle); + fprintf(stderr,"Type:"); + fprintf(stderr, + c->type == C_ROOT ? "Root|" : + c->type == C_OUTPUT ? "Output|" : + c->type == C_WORKSPACE ? "Workspace|" : + c->type == C_CONTAINER ? "Container|" : + c->type == C_VIEW ? "View|" : "Unknown|"); + fprintf(stderr,"layout:"); + fprintf(stderr, + c->layout == L_NONE ? "NONE|" : + c->layout == L_HORIZ ? "Horiz|": + c->layout == L_VERT ? "Vert|": + c->layout == L_STACKED ? "Stacked|": + c->layout == L_FLOATING ? "Floating|": + "Unknown|"); + fprintf(stderr, "w:%.f|h:%.f|", c->width, c->height); + fprintf(stderr, "x:%.f|y:%.f|", c->x, c->y); + fprintf(stderr, "g:%d|",c->gaps); + fprintf(stderr, "vis:%c|", c->visible?'t':'f'); + fprintf(stderr, "name:%.16s|", c->name); + fprintf(stderr, "children:%d\n",c->children?c->children->length:0); +} +void layout_log(const swayc_t *c, int depth) { + if (L_DEBUG > v) return; + int i, d; + int e = c->children ? c->children->length : 0; + container_log(c); + if (e) { + for (i = 0; i < e; ++i) { + fputc('|',stderr); + for (d = 0; d < depth; ++d) fputc('-', stderr); + layout_log(c->children->items[i], depth + 1); + } + } + if (c->type == C_WORKSPACE) { + e = c->floating?c->floating->length:0; + if (e) { + for (i = 0; i < e; ++i) { + fputc('|',stderr); + for (d = 0; d < depth; ++d) fputc('=', stderr); + layout_log(c->floating->items[i], depth + 1); + } + } + } +} + +const char *swayc_type_string(enum swayc_types type) { + return type == C_ROOT ? "ROOT" : + type == C_OUTPUT ? "OUTPUT" : + type == C_WORKSPACE ? "WORKSPACE" : + type == C_CONTAINER ? "CONTAINER" : + type == C_VIEW ? "VIEW" : + "UNKNOWN"; +} + +// Like sway_log, but also appends some info about given container to log output. +void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ...) { + sway_assert(cont, "swayc_log: no container ..."); + va_list args; + va_start(args, format); + char *txt = malloc(128); + vsprintf(txt, format, args); + va_end(args); + + char *debug_txt = malloc(32); + snprintf(debug_txt, 32, "%s '%s'", swayc_type_string(cont->type), cont->name); + + sway_log(verbosity, "%s (%s)", txt, debug_txt); + free(txt); + free(debug_txt); +} + +/* XXX:DEBUG:XXX */ diff --git a/swaybg/CMakeLists.txt b/swaybg/CMakeLists.txt index 89d8afde4..9351441af 100644 --- a/swaybg/CMakeLists.txt +++ b/swaybg/CMakeLists.txt @@ -9,14 +9,20 @@ WAYLAND_ADD_PROTOCOL_CLIENT(proto-xdg-shell "xdg-shell.xml" xdg-shell) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "../bin/") include_directories( - ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/../include ${WAYLAND_CLIENT_INCLUDE_DIR} ${CAIRO_INCLUDE_DIRS} ${PANGO_INCLUDE_DIRS} ) +FILE(GLOB sources ${PROJECT_SOURCE_DIR}/*.c) +FILE(GLOB wl_sources ${PROJECT_SOURCE_DIR}/../wayland/*.c) +FILE(GLOB common ${PROJECT_SOURCE_DIR}/../common/*.c) + add_executable(swaybg - main.c + ${sources} + ${wl_sources} + ${common} ) TARGET_LINK_LIBRARIES(swaybg ${WAYLAND_CLIENT_LIBRARIES} ${CAIRO_LIBRARIES} ${PANGO_LIBRARIES}) diff --git a/swaybg/main.c b/swaybg/main.c index 4a8ef5229..1b4af5506 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -1,6 +1,27 @@ #include +#include +#include +#include "client.h" +#include "log.h" + +struct client_state *state; + +void sway_terminate(void) { + client_teardown(state); + exit(1); +} int main(int argc, char **argv) { - printf("Hello world"); + init_log(L_INFO); + state = client_setup(); + + do { + if (!client_prerender(state)) continue; + cairo_set_source_rgb(state->cairo, 255, 0, 0); + cairo_rectangle(state->cairo, 0, 0, 100, 100); + cairo_fill(state->cairo); + } while (client_render(state)); + + client_teardown(state); return 0; } diff --git a/wayland/client.c b/wayland/client.c new file mode 100644 index 000000000..c62f92bd9 --- /dev/null +++ b/wayland/client.c @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "client.h" +#include "list.h" +#include "log.h" + +static void display_handle_mode(void *data, struct wl_output *wl_output, + uint32_t flags, int32_t width, int32_t height, int32_t refresh) { + struct output_state *state = data; + if (flags & WL_OUTPUT_MODE_CURRENT) { + state->flags = flags; + state->width = width; + state->height = height; + } +} + +static void display_handle_geometry(void *data, struct wl_output *wl_output, + int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, + int32_t subpixel, const char *make, const char *model, int32_t transform) { + // this space intentionally left blank +} + +static void display_handle_done(void *data, struct wl_output *wl_output) { + // this space intentionally left blank +} + +static void display_handle_scale(void *data, struct wl_output *wl_output, int32_t factor) { + // this space intentionally left blank +} + +static const struct wl_output_listener output_listener = { + .mode = display_handle_mode, + .geometry = display_handle_geometry, + .done = display_handle_done, + .scale = display_handle_scale +}; + +static void registry_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { + struct client_state *state = data; + + if (strcmp(interface, wl_compositor_interface.name) == 0) { + state->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, version); + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + state->shm = wl_registry_bind(registry, name, &wl_shm_interface, version); + } else if (strcmp(interface, wl_shell_interface.name) == 0) { + state->shell = wl_registry_bind(registry, name, &wl_shell_interface, version); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + state->seat = wl_registry_bind(registry, name, &wl_seat_interface, version); + state->pointer = wl_seat_get_pointer(state->seat); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + struct wl_output *output = wl_registry_bind(registry, name, &wl_output_interface, version); + struct output_state *ostate = malloc(sizeof(struct output_state)); + ostate->output = output; + wl_output_add_listener(output, &output_listener, ostate); + list_add(state->outputs, ostate); + } +} + +static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) { + // this space intentionally left blank +} + +static const struct wl_registry_listener registry_listener = { + .global = registry_global, + .global_remove = registry_global_remove +}; + +static int create_pool_file(size_t size) { + static const char template[] = "/swaybg-XXXXXX"; + const char *path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + return -1; + } + + int ts = (path[strlen(path) - 1] == '/'); + + char *name = malloc( + strlen(template) + + strlen(path) + + (ts ? 1 : 0) + 1); + sprintf(name, "%s%s%s", path, ts ? "" : "/", template); + + int fd = mkstemp(name); + free(name); + + if (fd < 0) { + return -1; + } + + if (ftruncate(fd, size) < 0) { + close(fd); + return -1; + } + + return fd; +} + +struct buffer *create_buffer(struct client_state *state, + int32_t width, int32_t height, uint32_t format) { + + struct buffer *buf = malloc(sizeof(struct buffer)); + memset(buf, 0, sizeof(struct buffer)); + uint32_t stride = width * 4; + uint32_t size = stride * height; + + int fd = create_pool_file(size); + void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + buf->pool = wl_shm_create_pool(state->shm, fd, size); + buf->buffer = wl_shm_pool_create_buffer(buf->pool, 0, width, height, stride, format); + + state->cairo_surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, width, height, stride); + state->cairo = cairo_create(state->cairo_surface); + state->pango = pango_cairo_create_context(state->cairo); + + sway_log(L_INFO, "%p %p", buf->pool, buf->buffer); + return buf; +} + +struct client_state *client_setup(void) { + struct client_state *state = malloc(sizeof(struct client_state)); + memset(state, 0, sizeof(struct client_state)); + state->outputs = create_list(); + + state->display = wl_display_connect(NULL); + if (!state->display) { + sway_log(L_ERROR, "Error opening display"); + client_teardown(state); + return NULL; + } + + struct wl_registry *registry = wl_display_get_registry(state->display); + wl_registry_add_listener(registry, ®istry_listener, state); + wl_display_roundtrip(state->display); // globals + wl_display_roundtrip(state->display); // listeners + wl_registry_destroy(registry); + + state->buffer = create_buffer(state, 100, 100, WL_SHM_FORMAT_ARGB8888); + state->surface = wl_compositor_create_surface(state->compositor); + state->shell_surface = wl_shell_get_shell_surface(state->shell, state->surface); + wl_shell_surface_set_toplevel(state->shell_surface); + + wl_surface_damage(state->surface, 0, 0, 100, 100); + wl_surface_attach(state->surface, state->buffer->buffer, 0, 0); + wl_surface_commit(state->surface); + + return state; +} + +int client_prerender(struct client_state *state) { + wl_display_dispatch_pending(state->display); + if (wl_display_flush(state->display) < 0 && errno != EAGAIN) { + return 0; + } + return 1; +} + +int client_render(struct client_state *state) { + return wl_display_dispatch(state->display) != -1; +} + +void client_teardown(struct client_state *state) { + if (state->pointer) { + wl_pointer_destroy(state->pointer); + } + if (state->seat) { + wl_seat_destroy(state->seat); + } + if (state->shell) { + wl_shell_destroy(state->shell); + } + if (state->shm) { + wl_shm_destroy(state->shm); + } + if (state->compositor) { + wl_compositor_destroy(state->compositor); + } + if (state->display) { + wl_display_disconnect(state->display); + } + if (state->outputs) { + // TODO: Free the outputs themselves + list_free(state->outputs); + } +}