#include #include #include #include #include #include #include #include #include #include #include #include #include char *argv0; #include "wayland.h" #include "drw.h" #include "xdg-shell-client-protocol.h" #include "arg.h" #include "util.h" #include "term.h" #include "win.h" /* macro definitions */ #define IS_SET(flag) ((win.mode & (flag)) != 0) #define match_then_bind(obj, inter, ver) \ if (strcmp(interface, inter.name) == 0) { \ obj = wl_registry_bind(registry, name, &inter, ver); #define end_match } #define or_match } else /* struct definitions */ typedef struct { unsigned width; unsigned height; int geometry_changed; struct { struct wl_surface *wl; struct xdg_surface *xdg; struct xdg_toplevel *toplevel; } surface; Canvas *canvas; FontCache *fontcache; int mode; } Window; /* function prototypes */ static void setup(); static void cleanup(); static void run(); static void usage(); static void buffer_release(void *data, struct wl_buffer *buffer); static void draw_frame(); static void commit_surface(); static void surface_configure(void *data, struct xdg_surface *surface, uint32_t serial); static void toplevel_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *clients); static void toplevel_close(void *data, struct xdg_toplevel *toplevel); static void wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial); static void registry_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version); static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name); static void seat_capabilities(void *dat, struct wl_seat *seat, uint32_t capabilities); static void keyboard_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size); static void keyboard_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t client); static void keyboard_modifiers(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group); static void keyboard_repeat_info(void *data, struct wl_keyboard *keyboard, int32_t rate, int32_t delay); static void handle_keyboard_event(); static void pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y); static void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface); static void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y); static void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state); static void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value); static void dummy() {} /* global variables */ static const struct wl_registry_listener registry_listener = { .global = registry_global, .global_remove = registry_global_remove, }; static const struct wl_buffer_listener buffer_listener = { .release = buffer_release, }; static const struct xdg_surface_listener surface_listener = { .configure = surface_configure, }; static const struct xdg_toplevel_listener toplevel_listener = { .configure = toplevel_configure, .close = toplevel_close, .configure_bounds = dummy, .wm_capabilities = dummy }; static const struct xdg_wm_base_listener wm_base_listener = { .ping = wm_base_ping, }; static const struct wl_seat_listener seat_listener = { .capabilities = seat_capabilities, .name = dummy }; static const struct wl_keyboard_listener keyboard_listener = { .keymap = keyboard_keymap, .key = keyboard_key, .modifiers = keyboard_modifiers, .repeat_info = keyboard_repeat_info, .enter = dummy, .leave = dummy, }; const struct wl_pointer_listener pointer_listener = { .enter = pointer_enter, .leave = pointer_leave, .motion = pointer_motion, .button = pointer_button, .axis = pointer_axis, .axis_discrete = dummy, .axis_source = dummy, .axis_stop = dummy, .axis_value120 = dummy, .frame = dummy, }; Client client = { 0 }; Window win = { 0 }; int running = 1; static char *opt_font = 0; static char *opt_name = 0; static char *opt_io = 0; static char *opt_line = 0; static char *opt_title = 0; static char **opt_cmd = 0; #include "config.h" /* function implementation */ void wbell() { fprintf(stderr, "BELL\n"); } void wclipcopy() { fprintf(stderr, "CLIPCOPY\n"); } void wdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) { fprintf(stderr, "DRAW CURSOR: cx: %i, cy: %i, ox: %i, oy: %i\n", cx, cy, ox, oy); draw_rect( win.canvas, ox * win.fontcache->box.width, oy * win.fontcache->box.height, win.fontcache->box.width, win.fontcache->box.height, 0x0 ); draw_char( win.canvas, win.fontcache, og.u, ox * win.fontcache->box.width, oy * win.fontcache->box.height + win.fontcache->fontsize, 0xffffffff ); draw_rect( win.canvas, cx * win.fontcache->box.width, cy * win.fontcache->box.height, win.fontcache->box.width, win.fontcache->box.height, 0xffffffff ); } void wdrawline(Line line, int x1, int y1, int x2) { int x; fprintf(stderr, "DRAW LINE: x1: %i, y1: %i, x2: %i\n"); draw_rect( win.canvas, x1 * win.fontcache->box.width, y1 * win.fontcache->box.height, (x2 - x1) * win.fontcache->box.width, win.fontcache->box.height, 0 ); fprintf(stderr, " line: "); for (x = x1; x < x2; x++) { draw_char( win.canvas, win.fontcache, line[x].u, x * win.fontcache->box.width, y1 * win.fontcache->box.height + win.fontcache->fontsize, 0xffffffff ); } } void wfinishdraw() { fprintf(stderr, "FINISH DRAW\n"); } void wloadcols() { fprintf(stderr, "LOADCOLS"); } int wsetcolorname(int, const char *) { fprintf(stderr, "SET COLOR NAME\n"); return 1; } int wgetcolor(int, unsigned char *, unsigned char *, unsigned char *) { fprintf(stderr, "GET COLOR\n"); return 1; } void wseticontitle(char *title) { fprintf(stderr, "SET ICON TITLE: %s\n", title); } void wsettitle(char *title) { fprintf(stderr, "SET TITLE: %s\n", title); } int wsetcursor(int cursor) { fprintf(stderr, "SET CURSOR: %i\n", cursor); return 1; } void wsetmode(int set, unsigned int flag) { fprintf(stderr, "SET MODE: %i, %u\n", set, flag); } void wsetpointermotion(int set) { fprintf(stderr, "SET POINTER MOTION: %i\n", set); } void wsetsel(char *str) { fprintf(stderr, "SET SEL: %s\n", str); } int wstartdraw() { return 1; //return IS_SET(MODE_VISIBLE); } void wximspot(int, int) {} void buffer_release(void *data, struct wl_buffer *buffer) { wl_buffer_destroy(buffer); } void draw_frame() { draw_rect(win.canvas, 0, 0, win.width, win.height, 0xff000000); } void commit_surface() { push_buffer(win.canvas); wl_surface_attach(win.surface.wl, win.canvas->buffer, 0, 0); wl_surface_damage_buffer(win.surface.wl, 0, 0, win.width, win.height); wl_surface_commit(win.surface.wl); } void surface_configure(void *data, struct xdg_surface *surface, uint32_t serial) { int first_canvas = win.canvas == 0; xdg_surface_ack_configure(surface, serial); if (win.geometry_changed || first_canvas) { if (win.canvas != 0) { free_drw(win.canvas); } win.canvas = create_drw(client.shm, win.width, win.height); draw(); win.geometry_changed = 0; } if (first_canvas) draw_frame(); commit_surface(); } void toplevel_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *clients) { if (width != win.width && height != win.height) win.geometry_changed = 1; if (width == 0 || height == 0) { win.width = 800; win.height = 600; } else { win.width = width; win.height = height; } } void toplevel_close(void *data, struct xdg_toplevel *toplevel) { running = 0; } void wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial) { xdg_wm_base_pong(wm_base, serial); } void registry_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { match_then_bind(client.shm, wl_shm_interface, 1) or_match match_then_bind(client.compositor, wl_compositor_interface, 4) or_match match_then_bind(client.wm_base, xdg_wm_base_interface, 1) or_match match_then_bind(client.seat, wl_seat_interface, 7) wl_seat_add_listener(client.seat, &seat_listener, &client); end_match } void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) { /* TODO: Check if this is necessary */ } void seat_capabilities(void *data, struct wl_seat *seat, uint32_t capabilities) { int has_keyboard = capabilities & WL_SEAT_CAPABILITY_KEYBOARD; if (has_keyboard && client.kb.keyboard == 0) { client.kb.keyboard = wl_seat_get_keyboard(client.seat); wl_keyboard_add_listener(client.kb.keyboard, &keyboard_listener, &client); } else { wl_keyboard_release(client.kb.keyboard); client.kb.keyboard = 0; } } void keyboard_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size) { char *key_shm; struct xkb_keymap *keymap; struct xkb_state *state; if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) return; key_shm = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0); if (key_shm == MAP_FAILED) return; keymap = xkb_keymap_new_from_string(client.kb.context, key_shm, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS ); munmap(key_shm, size); close(fd); state = xkb_state_new(keymap); xkb_keymap_unref(client.kb.keymap); xkb_state_unref(client.kb.state); client.kb.state = state; client.kb.keymap = keymap; } void keyboard_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { struct itimerspec spec = { 0 }; uint32_t keycode = key + 8; client.kb.event.sym = xkb_state_key_get_one_sym(client.kb.state, keycode); client.kb.event.state = state; handle_keyboard_event(); if (client.kb.event.state == WL_KEYBOARD_KEY_STATE_PRESSED && client.kb.repeat.period >= 0) { spec.it_value.tv_sec = client.kb.repeat.delay / 1000; spec.it_value.tv_nsec = (client.kb.repeat.delay % 1000) * 1000000; timerfd_settime(client.kb.repeat.timer, 0, &spec, 0); } else if (client.kb.event.state == WL_KEYBOARD_KEY_STATE_RELEASED) { timerfd_settime(client.kb.repeat.timer, 0, &spec, 0); } } void keyboard_modifiers(void *data, struct wl_keyboard *keybaord, uint32_t serial, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) { xkb_state_update_mask(client.kb.state, depressed, latched, locked, 0, 0, group); client.kb.event.mods.alt = xkb_state_mod_name_is_active( client.kb.state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED ); client.kb.event.mods.ctrl = xkb_state_mod_name_is_active( client.kb.state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED ); client.kb.event.mods.shift = xkb_state_mod_name_is_active( client.kb.state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED ); } void keyboard_repeat_info(void *data, struct wl_keyboard *keyboard, int32_t rate, int32_t delay) { client.kb.repeat.delay = delay; if (rate > 0) client.kb.repeat.period = 1000 / rate; else client.kb.repeat.period = -1; } void handle_keyboard_event() { char buf[9] = { 0 }; int len; if (client.kb.event.state == WL_KEYBOARD_KEY_STATE_PRESSED) { if ((len = xkb_keysym_to_utf8(client.kb.event.sym, buf, 8))) { ttywrite(buf, strlen(buf), 1); } } } void pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y) { } void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) { } void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) { } void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { } void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { } void setup() { win.fontcache = create_font_cache(fonts, sizeof(fonts) / sizeof(FontPath), fontsize); client.kb.repeat.timer = timerfd_create(CLOCK_MONOTONIC, 0); client.kb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); client.display = wl_display_connect(0); client.registry = wl_display_get_registry(client.display); wl_registry_add_listener(client.registry, ®istry_listener, &client); wl_display_roundtrip(client.display); xdg_wm_base_add_listener(client.wm_base, &wm_base_listener, &client); wl_display_roundtrip(client.display); client.pointer.pointer = wl_seat_get_pointer(client.seat); wl_pointer_add_listener(client.pointer.pointer, &pointer_listener, &client); win.surface.wl = wl_compositor_create_surface(client.compositor); win.surface.xdg = xdg_wm_base_get_xdg_surface(client.wm_base, win.surface.wl); xdg_surface_add_listener(win.surface.xdg, &surface_listener, &win); win.surface.toplevel = xdg_surface_get_toplevel(win.surface.xdg); xdg_toplevel_add_listener(win.surface.toplevel, &toplevel_listener, &win); xdg_toplevel_set_title(win.surface.toplevel, "swt"); wl_surface_commit(win.surface.wl); } void run() { struct itimerspec spec = { 0 }; struct pollfd fds[3]; int redraw = 0; fds[0].fd = wl_display_get_fd(client.display); fds[0].events = POLLIN; fds[1].fd = client.kb.repeat.timer; fds[1].events = POLLIN; fds[2].fd = ttynew(opt_line, shell, opt_io, opt_cmd); fds[2].events = POLLIN; while (running) { redraw = 0; if (wl_display_flush(client.display) < 0) { if (errno == EAGAIN) continue; break; } if (poll(fds, sizeof(fds) / sizeof(*fds), -1) < 0) { if (errno == EAGAIN) continue; break; } /* handle ttyfd at first so we have the real current screen */ if (fds[2].revents & POLLIN) { ttyread(); redraw = 1; } if (fds[0].revents & POLLIN) { if (wl_display_dispatch(client.display) < 0) { running = 0; } } if (fds[1].revents & POLLIN) { handle_keyboard_event(); spec.it_value.tv_sec = client.kb.repeat.period / 1000; spec.it_value.tv_nsec = (client.kb.repeat.period % 1000) * 1000000; timerfd_settime(client.kb.repeat.timer, 0, &spec, 0); redraw = 1; } if (redraw) { draw(); commit_surface(); } } } void cleanup() { wl_display_disconnect(client.display); } void usage(void) { die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" " [-n name] [-o file]\n" " [-T title] [-t title] [-w windowid]" " [[-e] command [args ...]]\n" " %s [-aiv] [-c class] [-f font] [-g geometry]" " [-n name] [-o file]\n" " [-T title] [-t title] [-w windowid] -l line" " [stty_args ...]\n", argv0, argv0); } int main(int argc, char *argv[]) { ARGBEGIN { case 'a': allowaltscreen = 0; break; break; case 'e': if (argc > 0) --argc, ++argv; goto run; case 'f': opt_font = EARGF(usage()); break; case 'o': opt_io = EARGF(usage()); break; case 'l': opt_line = EARGF(usage()); break; case 'n': opt_name = EARGF(usage()); break; case 't': case 'T': opt_title = EARGF(usage()); break; case 'v': die("%s " VERSION "\n", argv0); break; default: usage(); } ARGEND; run: if (argc > 0) /* eat all remaining arguments */ opt_cmd = argv; setlocale(LC_CTYPE, ""); tnew(cols, rows); setup(); run(); cleanup(); return 0; }