diff --git a/build.sh b/build.sh index e67bf69..bf37923 100755 --- a/build.sh +++ b/build.sh @@ -1 +1 @@ -gcc compositor.c common/shm.c protocols/xdg-shell-protocol.c -o compositor -lwayland-client -lrt -g +gcc compositor.c pointer.c keyboard.c common/shm.c protocols/xdg-shell-protocol.c -o compositor -lwayland-client -lrt -lxkbcommon -g diff --git a/common/state.h b/common/state.h new file mode 100644 index 0000000..4ac3f62 --- /dev/null +++ b/common/state.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include +#include + +enum pointer_event_mask +{ + POINTER_EVENT_ENTER = 1 << 0, + POINTER_EVENT_LEAVE = 1 << 1, + POINTER_EVENT_MOTION = 1 << 2, + POINTER_EVENT_BUTTON = 1 << 3, + POINTER_EVENT_AXIS = 1 << 4, + POINTER_EVENT_AXIS_SOURCE = 1 << 5, + POINTER_EVENT_AXIS_STOP = 1 << 6, + POINTER_EVENT_AXIS_DISCRETE = 1 << 7, +}; + +struct pointer_event +{ + uint32_t event_mask; + wl_fixed_t surface_x, surface_y; + uint32_t button, state; + uint32_t time; + uint32_t serial; + struct + { + bool valid; + wl_fixed_t value; + int32_t discrete; + } axes[2]; + uint32_t axis_source; +}; + +struct client_state +{ + /* Globals */ + struct wl_display *wl_display; + struct wl_registry *wl_registry; + struct wl_shm *wl_shm; + struct wl_compositor *wl_compositor; + struct xdg_wm_base *xdg_wm_base; + struct wl_seat *wl_seat; + + /* Objects */ + struct wl_surface *wl_surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + struct wl_keyboard *wl_keyboard; + struct wl_pointer *wl_pointer; + struct wl_touch *wl_touch; + + /* State */ + float offset; + uint32_t last_frame; + struct pointer_event pointer_event; + struct xkb_state *xkb_state; + struct xkb_context *xkb_context; + struct xkb_keymap *xkb_keymap; +}; diff --git a/compositor b/compositor index a5700ee..29e109e 100755 Binary files a/compositor and b/compositor differ diff --git a/compositor.c b/compositor.c index 0551a85..ffa0f8c 100644 --- a/compositor.c +++ b/compositor.c @@ -1,40 +1,33 @@ #include "common/shm.h" +#include "common/state.h" +#include "pointer.h" +#include "keyboard.h" #include "protocols/xdg-shell-client-protocol.h" +#include #include #include #include #include -#include +#include -struct client_state -{ - /* Globals */ - struct wl_display *wl_display; - struct wl_registry *wl_registry; - struct wl_shm *wl_shm; - struct wl_compositor *wl_compositor; - struct xdg_wm_base *xdg_wm_base; - - /* Objects */ - struct wl_surface *wl_surface; - struct xdg_surface *xdg_surface; - struct xdg_toplevel *xdg_toplevel; -}; +static const struct wl_callback_listener wl_surface_frame_listener; static struct wl_buffer *draw_frame(struct client_state *state); -static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, +static void registry_handle_global(void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version) { struct client_state *state = data; if (strcmp(interface, wl_compositor_interface.name) == 0) { - state->wl_compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 5); + state->wl_compositor = wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4); } else if (strcmp(interface, wl_shm_interface.name) == 0) { - state->wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + state->wl_shm = wl_registry_bind(wl_registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { - state->xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 4); + state->xdg_wm_base = wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 3); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + state->wl_seat = wl_registry_bind(wl_registry, name, &wl_seat_interface, 7); } } @@ -52,7 +45,7 @@ static void wm_base_handle_ping(void *data, struct xdg_wm_base *xdg_wm_base, uin { struct client_state *state = data; - // TODO ! + xdg_wm_base_pong(xdg_wm_base, serial); } static void surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) @@ -66,6 +59,61 @@ static void surface_handle_configure(void *data, struct xdg_surface *xdg_surface wl_surface_commit(state->wl_surface); } +static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) +{ + fprintf(stderr, "seat name: %s\n", name); +} + +static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) +{ + struct client_state *state = data; + + bool have_pointer = capabilities & WL_SEAT_CAPABILITY_POINTER; + + if (have_pointer && state->wl_pointer == NULL) { + state->wl_pointer = wl_seat_get_pointer(state->wl_seat); + wl_pointer_add_listener(state->wl_pointer, &wl_pointer_listener, state); + } else if (!have_pointer && state->wl_pointer != NULL) { + wl_pointer_release(state->wl_pointer); + state->wl_pointer = NULL; + } + + bool have_keyboard = capabilities & WL_SEAT_CAPABILITY_KEYBOARD; + + if (have_keyboard && state->wl_keyboard == NULL) { + state->wl_keyboard = wl_seat_get_keyboard(state->wl_seat); + wl_keyboard_add_listener(state->wl_keyboard, &wl_keyboard_listener, state); + } else if (!have_keyboard && state->wl_keyboard != NULL) { + wl_keyboard_release(state->wl_keyboard); + state->wl_keyboard = NULL; + } +} + +static void wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) +{ + /* Destroy this callback */ + wl_callback_destroy(cb); + + /* Request another frame */ + struct client_state *state = data; + cb = wl_surface_frame(state->wl_surface); + wl_callback_add_listener(cb, &wl_surface_frame_listener, state); + + /* Update scroll amount at 24 pixels per second */ + if (state->last_frame != 0) { + int elapsed = time - state->last_frame; + state->offset += elapsed / 1000.0 * 24; + } + + /* Submit a frame for this event */ + struct wl_buffer *buffer = draw_frame(state); + wl_surface_attach(state->wl_surface, buffer, 0, 0); + wl_surface_damage_buffer(state->wl_surface, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_commit(state->wl_surface); + + state->last_frame = time; +} + static const struct wl_registry_listener wl_registry_listener = { .global = registry_handle_global, .global_remove = registry_handle_global_remove, @@ -83,6 +131,15 @@ static const struct xdg_surface_listener xdg_surface_listener = { .configure = surface_handle_configure, }; +static const struct wl_seat_listener wl_seat_listener = { + .name = seat_handle_name, + .capabilities = seat_handle_capabilities, +}; + +static const struct wl_callback_listener wl_surface_frame_listener = { + .done = wl_surface_frame_done, +}; + static void wl_buffer_release(void *data, struct wl_buffer *wl_buffer) { /* Sent by the compositor when it's no longer using this buffer */ @@ -98,7 +155,6 @@ static struct wl_buffer *draw_frame(struct client_state *state) const int width = 640, height = 480; int stride = width * 4; int size = stride * height; - int offset = 0; int fd = allocate_shm_file(size); if (fd == -1) { @@ -121,10 +177,11 @@ static struct wl_buffer *draw_frame(struct client_state *state) // memset(pixels, 255, width * height * 4); // Checkerboard pattern - uint32_t *pixels = (uint32_t *)&data[offset]; + int offset = (int)state->offset % 8; + uint32_t *pixels = (uint32_t *)data; for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { - if ((x + y / 8 * 8) % 16 < 8) { + if (((x + offset) + (y + offset) / 8 * 8) % 16 < 8) { pixels[y * width + x] = 0xFF666666; } else { pixels[y * width + x] = 0xFFEEEEEE; @@ -142,25 +199,27 @@ int main(int argc, char *argv[]) struct client_state state = {0}; state.wl_display = wl_display_connect(NULL); - + state.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); state.wl_registry = wl_display_get_registry(state.wl_display); - wl_registry_add_listener(state.wl_registry, &wl_registry_listener, &state); wl_display_roundtrip(state.wl_display); - state.wl_surface = wl_compositor_create_surface(state.wl_compositor); + wl_seat_add_listener(state.wl_seat, &wl_seat_listener, &state); + state.wl_surface = wl_compositor_create_surface(state.wl_compositor); wl_shm_add_listener(state.wl_shm, &wl_shm_listener, &state); state.xdg_surface = xdg_wm_base_get_xdg_surface(state.xdg_wm_base, state.wl_surface); xdg_surface_add_listener(state.xdg_surface, &xdg_surface_listener, &state); state.xdg_toplevel = xdg_surface_get_toplevel(state.xdg_surface); - xdg_toplevel_set_title(state.xdg_toplevel, "Example client"); wl_surface_commit(state.wl_surface); + struct wl_callback *cb = wl_surface_frame(state.wl_surface); + wl_callback_add_listener(cb, &wl_surface_frame_listener, &state); + while (wl_display_dispatch(state.wl_display)) { /* This space deliberately left blank */ } diff --git a/keyboard.c b/keyboard.c new file mode 100644 index 0000000..bd0c877 --- /dev/null +++ b/keyboard.c @@ -0,0 +1,83 @@ +#include "keyboard.h" +#include "common/state.h" + +#include +#include +#include +#include +#include + +const struct wl_keyboard_listener wl_keyboard_listener = {.enter = wl_keyboard_enter, + .key = wl_keyboard_key, + .keymap = wl_keyboard_keymap, + .leave = wl_keyboard_leave, + .modifiers = wl_keyboard_modifiers, + .repeat_info = wl_keyboard_repeat_info}; + +void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) +{ + struct client_state *client_state = data; + assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1); + + char *map_shm = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + assert(map_shm != MAP_FAILED); + + struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string(client_state->xkb_context, map_shm, + XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(map_shm, size); + close(fd); + + struct xkb_state *xkb_state = xkb_state_new(xkb_keymap); + xkb_keymap_unref(client_state->xkb_keymap); + xkb_state_unref(client_state->xkb_state); + client_state->xkb_keymap = xkb_keymap; + client_state->xkb_state = xkb_state; +} + +void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, + struct wl_array *keys) +{ + struct client_state *client_state = data; + fprintf(stderr, "keyboard enter; keys pressed are:\n"); + uint32_t *key; + wl_array_for_each(key, keys) + { + char buf[128]; + xkb_keysym_t sym = xkb_state_key_get_one_sym(client_state->xkb_state, *key + 8); + xkb_keysym_get_name(sym, buf, sizeof(buf)); + fprintf(stderr, "sym: %-12s (%d), ", buf, sym); + xkb_state_key_get_utf8(client_state->xkb_state, *key + 8, buf, sizeof(buf)); + fprintf(stderr, "utf8: '%s'\n", buf); + } +} + +void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) +{ + fprintf(stderr, "keyboard leave\n"); +} + +void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, + uint32_t state) +{ + struct client_state *client_state = data; + char buf[128]; + uint32_t keycode = key + 8; + xkb_keysym_t sym = xkb_state_key_get_one_sym(client_state->xkb_state, keycode); + xkb_keysym_get_name(sym, buf, sizeof(buf)); + const char *action = state == WL_KEYBOARD_KEY_STATE_PRESSED ? "press" : "release"; + fprintf(stderr, "key %s: sym: %-12s (%d), ", action, buf, sym); + xkb_state_key_get_utf8(client_state->xkb_state, keycode, buf, sizeof(buf)); + fprintf(stderr, "utf8: '%s'\n", buf); +} + +void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, + uint32_t mods_latched, uint32_t mods_locked, uint32_t group) +{ + struct client_state *client_state = data; + xkb_state_update_mask(client_state->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); +} + +void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) +{ + /* Left as an exercise for the reader */ +} diff --git a/keyboard.h b/keyboard.h new file mode 100644 index 0000000..382a598 --- /dev/null +++ b/keyboard.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size); +void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, + struct wl_array *keys); +void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface); +void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, + uint32_t state); +void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, + uint32_t mods_latched, uint32_t mods_locked, uint32_t group); +void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay); + +extern const struct wl_keyboard_listener wl_keyboard_listener; diff --git a/pointer.c b/pointer.c new file mode 100644 index 0000000..7ce605e --- /dev/null +++ b/pointer.c @@ -0,0 +1,151 @@ +#include "pointer.h" +#include "common/state.h" + +#include +#include + +const struct wl_pointer_listener wl_pointer_listener = { + .enter = wl_pointer_enter, + .leave = wl_pointer_leave, + .motion = wl_pointer_motion, + .button = wl_pointer_button, + .axis = wl_pointer_axis, + .frame = wl_pointer_frame, + .axis_source = wl_pointer_axis_source, + .axis_stop = wl_pointer_axis_stop, + .axis_discrete = wl_pointer_axis_discrete, +}; + +void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, + wl_fixed_t surface_x, wl_fixed_t surface_y) +{ + struct client_state *client_state = data; + client_state->pointer_event.event_mask |= POINTER_EVENT_ENTER; + client_state->pointer_event.serial = serial; + client_state->pointer_event.surface_x = surface_x; + client_state->pointer_event.surface_y = surface_y; +} + +void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) +{ + struct client_state *client_state = data; + client_state->pointer_event.serial = serial; + client_state->pointer_event.event_mask |= POINTER_EVENT_LEAVE; +} + +void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + struct client_state *client_state = data; + client_state->pointer_event.event_mask |= POINTER_EVENT_MOTION; + client_state->pointer_event.time = time; + client_state->pointer_event.surface_x = surface_x; + client_state->pointer_event.surface_y = surface_y; +} + +void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, + uint32_t state) +{ + struct client_state *client_state = data; + client_state->pointer_event.event_mask |= POINTER_EVENT_BUTTON; + client_state->pointer_event.time = time; + client_state->pointer_event.serial = serial; + client_state->pointer_event.button = button; + client_state->pointer_event.state = state; +} + +void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) +{ + struct client_state *client_state = data; + client_state->pointer_event.event_mask |= POINTER_EVENT_AXIS; + client_state->pointer_event.time = time; + client_state->pointer_event.axes[axis].valid = true; + client_state->pointer_event.axes[axis].value = value; +} + +void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) +{ + struct client_state *client_state = data; + struct pointer_event *event = &client_state->pointer_event; + fprintf(stderr, "pointer frame @ %d: ", event->time); + + if (event->event_mask & POINTER_EVENT_ENTER) { + fprintf(stderr, "entered %f, %f ", wl_fixed_to_double(event->surface_x), wl_fixed_to_double(event->surface_y)); + } + + if (event->event_mask & POINTER_EVENT_LEAVE) { + fprintf(stderr, "leave"); + } + + if (event->event_mask & POINTER_EVENT_MOTION) { + fprintf(stderr, "motion %f, %f ", wl_fixed_to_double(event->surface_x), wl_fixed_to_double(event->surface_y)); + } + + if (event->event_mask & POINTER_EVENT_BUTTON) { + char *state = event->state == WL_POINTER_BUTTON_STATE_RELEASED ? "released" : "pressed"; + fprintf(stderr, "button %d %s ", event->button, state); + } + + uint32_t axis_events = + POINTER_EVENT_AXIS | POINTER_EVENT_AXIS_SOURCE | POINTER_EVENT_AXIS_STOP | POINTER_EVENT_AXIS_DISCRETE; + char *axis_name[2] = { + [WL_POINTER_AXIS_VERTICAL_SCROLL] = "vertical", + [WL_POINTER_AXIS_HORIZONTAL_SCROLL] = "horizontal", + }; + char *axis_source[4] = { + [WL_POINTER_AXIS_SOURCE_WHEEL] = "wheel", + [WL_POINTER_AXIS_SOURCE_FINGER] = "finger", + [WL_POINTER_AXIS_SOURCE_CONTINUOUS] = "continuous", + [WL_POINTER_AXIS_SOURCE_WHEEL_TILT] = "wheel tilt", + }; + if (event->event_mask & axis_events) { + for (size_t i = 0; i < 2; ++i) { + if (!event->axes[i].valid) { + continue; + } + fprintf(stderr, "%s axis ", axis_name[i]); + if (event->event_mask & POINTER_EVENT_AXIS) { + fprintf(stderr, "value %f ", wl_fixed_to_double(event->axes[i].value)); + } + if (event->event_mask & POINTER_EVENT_AXIS_DISCRETE) { + fprintf(stderr, "discrete %d ", event->axes[i].discrete); + } + if (event->event_mask & POINTER_EVENT_AXIS_SOURCE) { + fprintf(stderr, "via %s ", axis_source[event->axis_source]); + } + if (event->event_mask & POINTER_EVENT_AXIS_STOP) { + fprintf(stderr, "(stopped) "); + } + } + } + + fprintf(stderr, "\n"); + memset(event, 0, sizeof(*event)); +} + +void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) +{ + struct client_state *client_state = data; + client_state->pointer_event.event_mask |= POINTER_EVENT_AXIS_SOURCE; + client_state->pointer_event.axis_source = axis_source; +} + +void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) +{ + struct client_state *client_state = data; + client_state->pointer_event.time = time; + client_state->pointer_event.event_mask |= POINTER_EVENT_AXIS_STOP; + client_state->pointer_event.axes[axis].valid = true; +} + +void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) +{ + struct client_state *client_state = data; + client_state->pointer_event.event_mask |= POINTER_EVENT_AXIS_DISCRETE; + client_state->pointer_event.axes[axis].valid = true; + client_state->pointer_event.axes[axis].discrete = discrete; +} + +void wl_pointer_axis_value120(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t value120) +{ +} diff --git a/pointer.h b/pointer.h new file mode 100644 index 0000000..c78d63b --- /dev/null +++ b/pointer.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, + wl_fixed_t surface_x, wl_fixed_t surface_y); +void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface); +void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, + wl_fixed_t surface_y); +void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, + uint32_t state); +void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value); +void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer); +void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source); +void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis); +void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete); +void wl_pointer_axis_value120(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t value120); + +extern const struct wl_pointer_listener wl_pointer_listener; diff --git a/protocols/xdg-shell-client-protocol.h b/protocols/xdg-shell-client-protocol.h index 83d5a7d..026562a 100644 --- a/protocols/xdg-shell-client-protocol.h +++ b/protocols/xdg-shell-client-protocol.h @@ -5,7 +5,7 @@ #include #include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/protocols/xdg-shell-protocol.c b/protocols/xdg-shell-protocol.c index e68e61c..5af53fe 100644 --- a/protocols/xdg-shell-protocol.c +++ b/protocols/xdg-shell-protocol.c @@ -30,7 +30,7 @@ #include #include -#include +#include #ifndef __has_attribute # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */