#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 TRUERED(x) (((x) & 0xff0000) >> 16) #define TRUEGREEN(x) (((x) & 0xff00) >> 8) #define TRUEBLUE(x) (((x) & 0xff)) #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) #define FONTMODE(mode) (mode & ATTR_ITALIC ? (mode & ATTR_BOLD ? FONT_BOLD_ITALIC : FONT_ITALIC) : (mode & ATTR_BOLD ? FONT_BOLD : FONT_NORMAL)) #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 */ ushort sixd_to_8bit(int x); static void setup(); static void cleanup(); static void run(); static void usage(); static int matchmod(uint32_t mask, uint32_t mod); static char *kmap(); 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() { /* TODO */ } void wclipcopy() { /* TODO */ } void wdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) { draw_rect( win.canvas, borderpx + ox * win.fontcache->box.width, borderpx + oy * win.fontcache->box.height, win.fontcache->box.width, win.fontcache->box.height, colors[og.bg] ); win.fontcache->fonttype = FONTMODE(og.mode); draw_char( win.canvas, win.fontcache, og.u, borderpx + ox * win.fontcache->box.width, borderpx + oy * win.fontcache->box.height + win.fontcache->fontsize, colors[og.fg] ); draw_rect( win.canvas, borderpx + cx * win.fontcache->box.width, borderpx + cy * win.fontcache->box.height, win.fontcache->box.width, win.fontcache->box.height, colors[g.fg] ); win.fontcache->fonttype = FONTMODE(g.mode); draw_char( win.canvas, win.fontcache, g.u, borderpx + cx * win.fontcache->box.width, borderpx + cy * win.fontcache->box.height + win.fontcache->fontsize, colors[g.bg] ); } void wdrawline(Line line, int x1, int y1, int x2) { int x; draw_rect( win.canvas, borderpx + x1 * win.fontcache->box.width, borderpx + y1 * win.fontcache->box.height, x2 * win.fontcache->box.width, win.fontcache->box.height, colors[defaultbg] ); for (x = x1; x < x2; x++) { win.fontcache->fonttype = FONTMODE(line[x].mode); draw_rect( win.canvas, borderpx + x * win.fontcache->box.width, borderpx + y1 * win.fontcache->box.height, 10, win.fontcache->box.height, colors[line[x].bg] ); draw_char( win.canvas, win.fontcache, line[x].u, borderpx + x * win.fontcache->box.width, borderpx + y1 * win.fontcache->box.height + win.fontcache->fontsize, colors[line[x].fg] ); } } void wfinishdraw() { commit_surface(); } ushort sixd_to_8bit(int x) { return x == 0 ? 0 : 0x37 + 0x28 * x; } void wloadcols() { int i; int c; for (i = 0; i < sizeof(colors) / sizeof(uint32_t); ++i) { if (colors[i] == 0) { if (i < 6 * 6 * 6 + 16) { colors[i] = sixd_to_8bit(((i - 16) / 36) % 6) << 16 | sixd_to_8bit(((i - 16) / 6) % 6) << 8 | sixd_to_8bit(((i - 16) / 1) % 6); } else { c = 0x08 + 0x0a * (i - (6 * 6 * 6 * 6 + 16)); c &= 0xff; colors[i] = (c << 16) | (c << 8) | c; } } colors[i] |= 0xff000000; } } int wsetcolorname(int, const char *) { fprintf(stderr, "SET COLOR NAME\n"); return 1; } int wgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b) { if (!BETWEEN(x, 0, sizeof(colors) / sizeof(uint32_t))) return 1; *r = (colors[x] & 0xff0000) >> 16; *g = (colors[x] & 0x00ff00) >> 8; *b = (colors[x] & 0x0000ff); return 0; } void wseticontitle(char *title) { fprintf(stderr, "SET ICON TITLE: %s\n", title); } void wsettitle(char *title) { xdg_toplevel_set_title(win.surface.toplevel, title); } int wsetcursor(int cursor) { fprintf(stderr, "SET CURSOR: %i\n", cursor); return 1; } void wsetmode(int set, unsigned int flags) { int mode = win.mode; MODBIT(win.mode, set, flags); if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) redraw(); } 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 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, colors[defaultbg]); } 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_configure = win.canvas == 0; int ttywidth, ttyheight; xdg_surface_ack_configure(surface, serial); if (first_configure) commit_surface(); } void toplevel_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *clients) { int ttywidth, ttyheight; if (win.canvas == 0 || width != win.width || height != win.height) { if (width == 0 || height == 0) { win.width = 800; win.height = 600; } else { win.width = width; win.height = height; } if (win.canvas != 0) { free_drw(win.canvas); } win.canvas = create_drw(client.shm, win.width, win.height); draw_frame(); ttywidth = (win.width - 2 * borderpx) / win.fontcache->box.width; ttyheight = (win.height - 2 * borderpx) / win.fontcache->box.height; ttywidth = MAX(ttywidth, 1); ttyheight = MAX(ttyheight, 1); tresize(ttywidth, ttyheight); ttyresize(ttywidth, ttyheight); } } 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.mod = 0; client.kb.event.mod |= xkb_state_mod_name_is_active( client.kb.state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED ) * KEY_MOD_ALT; client.kb.event.mod = xkb_state_mod_name_is_active( client.kb.state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED ) * KEY_MOD_CTRL; client.kb.event.mod |= xkb_state_mod_name_is_active( client.kb.state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED ) * KEY_MOD_SHIFT; client.kb.event.mod |= xkb_state_mod_name_is_active( client.kb.state, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED ) * KEY_MOD_LOGO; } 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; } int matchmod(uint32_t mask, uint32_t mod) { return mask == KEY_MOD_ANY || mask == mod; } char * kmap() { Key *kp; for (kp = key; kp < key + LEN(key); kp++) { if (kp->k != client.kb.event.sym) continue; if (!matchmod(kp->mask, client.kb.event.mod)) continue; return kp->s; } return 0; } void handle_keyboard_event() { char buf[9] = { 0 }; char *customkey; int len; char c; if (client.kb.event.state == WL_KEYBOARD_KEY_STATE_PRESSED) { if ((customkey = kmap())) { ttywrite(customkey, strlen(customkey), 1); return; } if ((len = xkb_keysym_to_utf8(client.kb.event.sym, buf, 8))) { if (client.kb.event.mod & KEY_MOD_CTRL) { xkb_keysym_to_utf8(xkb_keysym_to_lower(client.kb.event.sym), buf, 8); buf[0] -= 'a' - 1; } 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.mode = MODE_VISIBLE; win.fontcache = create_font_cache(fonts, sizeof(fonts) / sizeof(FontPath), fontsize); wloadcols(); 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(); } } } 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; }