#include #include #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 wintotty(scale, type) (((int)scale < (int)(2 * borderpx)) ? 0 : (scale - 2 * borderpx) / win.fontcache->box.type) #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; int cursor; struct { unsigned width; unsigned height; } tty; int redraw; } Window; /* function prototypes */ ushort sixd_to_8bit(int x); static void setup(); static void cleanup(); static void run(); static void usage(); static void copy(); static void paste(); static int matchmod(uint32_t mask, uint32_t mod); static int execshortcut(); static char *kmap(); static void buffer_release(void *data, struct wl_buffer *buffer); static void draw_frame(); static uint32_t get_color(uint32_t i, ushort mode); static void draw_glyph(Glyph g, int x, int y); 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_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys); 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 keyboard_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface); 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 data_device_selection(void *data, struct wl_data_device *data_device, struct wl_data_offer *offer); static void data_device_data_offer(void *data, struct wl_data_device *data_device, struct wl_data_offer *offer); static void data_source_send(void *data, struct wl_data_source *source, const char *mime_type, int fd); static void data_source_cancelled(void *data, struct wl_data_source *source); 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 = keyboard_enter, .leave = keyboard_leave, }; static 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, }; static const struct wl_data_device_listener data_device_listener = { .data_offer = data_device_data_offer, .selection = data_device_selection }; static const struct wl_data_source_listener data_source_listener = { .send = data_source_send, .cancelled = data_source_cancelled }; static const struct wl_data_offer_listener data_offer_listener = { .offer = 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" static char clipboard[CLIPBOARD_SIZE] = ""; /* function implementation */ void wbell() { /* TODO */ } void wclipcopy() { copy(); } void copy() { struct wl_data_source *source = wl_data_device_manager_create_data_source(client.clipboard.data_device_manager); strcpy(clipboard, getsel()); wl_data_source_add_listener(source, &data_source_listener, 0); wl_data_source_offer(source, "text/plain"); wl_data_device_set_selection(client.clipboard.data_device, source, client.clipboard.keyboard_enter_serial); } void paste() { ttywrite(clipboard, strlen(clipboard), 1); } uint32_t get_color(uint32_t i, ushort mode) { if (!BETWEEN(i, 0, sizeof(colors) / sizeof(uint32_t))) { return i; } if ((mode & ATTR_BOLD) && i >= 0 && i < 8) { if (!BETWEEN(i + 8, 0, sizeof(colors) / sizeof(uint32_t))) { return i; } return colors[i + 8]; } return colors[i]; } void draw_glyph(Glyph g, int x, int y) { uint32_t bg; uint32_t fg; if (selected(x, y)) { g.mode ^= ATTR_REVERSE; } bg = (g.mode & ATTR_REVERSE) ? get_color(g.fg, 0) : get_color(g.bg, 0); fg = (g.mode & ATTR_REVERSE) ? get_color(g.bg, g.mode) : get_color(g.fg, g.mode); win.fontcache->fonttype = FONTMODE(g.mode); draw_rect( win.canvas, borderpx + x * win.fontcache->box.width, borderpx + y * win.fontcache->box.height, win.fontcache->box.width, win.fontcache->box.height, bg ); if (g.mode & ATTR_UNDERLINE) { draw_rect( win.canvas, borderpx + x * win.fontcache->box.width, borderpx + (y + 1) * win.fontcache->box.height - 1, win.fontcache->box.width, 1, fg ); } draw_char( win.canvas, win.fontcache, g.u, borderpx + x * win.fontcache->box.width, borderpx + y * win.fontcache->box.height + win.fontcache->fontsize, fg ); wl_surface_damage_buffer( win.surface.wl, borderpx + x * win.fontcache->box.width, borderpx + y * win.fontcache->box.height, win.fontcache->box.width, win.fontcache->box.height ); } void wdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) { if (IS_SET(MODE_HIDE)) return; draw_rect( win.canvas, borderpx + ox * win.fontcache->box.width, borderpx + oy * win.fontcache->box.height, win.fontcache->box.width, win.fontcache->box.height, get_color(og.bg, 0) ); draw_glyph(og, ox, oy); g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; switch(win.cursor) { case 0: /* Block */ case 1: case 2: g.bg = defaultfg; g.fg = defaultbg; draw_glyph(g, cx, cy); break; case 3: /* Underline */ case 4: draw_glyph(g, cx, cy); draw_rect( win.canvas, borderpx + cx * win.fontcache->box.width, borderpx + cy * win.fontcache->box.height + win.fontcache->box.height - cursorthickness, win.fontcache->box.width, cursorthickness, get_color(defaultcursorcolor, 0) ); break; case 5: /* Bar */ case 6: draw_glyph(g, cx, cy); draw_rect( win.canvas, borderpx + cx * win.fontcache->box.width, borderpx + cy * win.fontcache->box.height, cursorthickness, win.fontcache->box.height, get_color(defaultcursorcolor, 0) ); break; } } void wdrawline(Line line, int x1, int y1, int x2) { int x; for (x = x1; x < x2; x++) { draw_glyph(line[x], x, y1); } } 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 + 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 = (get_color(x, 0) & 0xff0000) >> 16; *g = (get_color(x, 0) & 0x00ff00) >> 8; *b = (get_color(x, 0) & 0x0000ff); return 0; } void wseticontitle(char *title) { wsettitle(title); } void wsettitle(char *title) { xdg_toplevel_set_title(win.surface.toplevel, title); } int wsetcursor(int cursor) { if (!BETWEEN(cursor, 0, 7)) return 0; win.cursor = cursor; return 1; } const char* modes[] = { "visible", "focused", "appkeypad", "mousebtn", "mousemotion", "reverse", "kbdlock", "hide", "appcursor", "mousesgr", "8bit", "blink", "fblink", "focus", "mousex10", "mousemany", "brcktpaste", "numlock", }; void wsetmode(int set, unsigned int flags) { int mode = win.mode; MODBIT(win.mode, set, flags); if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) win.redraw = 1; } 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) { fprintf(stderr, "BUFFER RELEASE\n"); wl_buffer_destroy(buffer); } void draw_frame() { draw_rect(win.canvas, 0, 0, win.width, win.height, get_color(defaultbg, 0)); } void commit_surface() { wl_surface_attach(win.surface.wl, win.canvas->buffer, 0, 0); wl_surface_commit(win.surface.wl); } void surface_configure(void *data, struct xdg_surface *surface, uint32_t serial) { int first_configure = win.canvas == 0; xdg_surface_ack_configure(surface, serial); if (first_configure) { wl_surface_attach(win.surface.wl, win.canvas->buffer, 0, 0); wl_surface_damage_buffer(win.surface.wl, 0, 0, win.width, win.height); commit_surface(); } } void toplevel_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *clients) { 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) { win.canvas = create_drw(client.shm, win.width, win.height); } else { resize_drw(win.canvas, width, height); } draw_frame(); win.tty.width = (win.width - 2 * borderpx) / win.fontcache->box.width; win.tty.height = (win.height - 2 * borderpx) / win.fontcache->box.height; win.tty.width = MAX(win.tty.width, 1); win.tty.height = MAX(win.tty.height, 1); tresize(win.tty.width, win.tty.height); ttyresize(win.tty.width, win.tty.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.clipboard.data_device_manager, wl_data_device_manager_interface, 3) 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) { fprintf(stderr, "wayland registry remove %i\n", 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_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { client.clipboard.keyboard_enter_serial = serial; } void keyboard_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) { struct itimerspec spec = { 0 }; timerfd_settime(client.kb.repeat.timer, 0, &spec, 0); win.redraw = 1; } 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; } int execshortcut() { Shortcut *sc; for (sc = shortcut; sc < shortcut + LEN(shortcut); sc++) { if (sc->k != client.kb.event.sym) continue; if (!matchmod(sc->mask, client.kb.event.mod)) continue; sc->func(); return 1; } return 0; } 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; if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) continue; if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2) continue; if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) continue; return kp->s; } return 0; } void handle_keyboard_event() { char buf[9] = { 0 }; char *customkey; int len; char c; if (IS_SET(MODE_KBDLOCK)) return; if (client.kb.event.state == WL_KEYBOARD_KEY_STATE_PRESSED) { if (execshortcut()) return; 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; } if (len == 1 && client.kb.event.state && client.kb.event.mod & KEY_MOD_ALT) { if (IS_SET(MODE_8BIT)) { if (*buf < 0177) { c = *buf | 0x80; len = utf8encode(c, buf); } } else { buf[1] = buf[0]; buf[0] = '\033'; len = 2; } } 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) { wl_pointer_set_cursor(client.pointer.pointer, serial, client.pointer.surface, client.pointer.image->hotspot_x, client.pointer.image->hotspot_y); } void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) { selextend( wintotty(client.pointer.position.x, width), wintotty(client.pointer.position.y, height), SEL_REGULAR, 1 ); } void reportpointer(int motion) { int len, btn, code; int x = client.pointer.position.x, y = client.pointer.position.y; int state = client.pointer.buttons.state; char buf[40]; static int ox, oy; if (motion) { if (x == ox && y == oy) return; if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) return; /* MODE_MOUSEMOTION: no reporting if no button is pressed */ if (IS_SET(MODE_MOUSEMOTION) && !state) return; /* Set btn to lowest-numbered pressed button, or 12 if no * buttons are pressed. */ btn = client.pointer.buttons.button - BTN_MOUSE + 1; code = 32; } else { btn = client.pointer.buttons.button - BTN_MOUSE + 1; /* Only buttons 1 through 11 can be encoded */ if (btn < 1 || btn > 11) return; if (!state) { /* MODE_MOUSEX10: no button release reporting */ if (IS_SET(MODE_MOUSEX10)) return; /* Don't send release events for the scroll wheel */ if (btn == 4 || btn == 5) return; } code = 0; } ox = x; oy = y; /* Encode btn into code. If no button is pressed for a motion event in * MODE_MOUSEMANY, then encode it as a release. */ if ((!IS_SET(MODE_MOUSESGR) && state) || btn == 12) code += 3; else if (btn >= 8) code += 128 + btn - 8; else if (btn >= 4) code += 64 + btn - 4; else code += btn - 1; if (!IS_SET(MODE_MOUSEX10)) { code += ((client.kb.event.mod & KEY_MOD_SHIFT) ? 4 : 0) + ((client.kb.event.mod & KEY_MOD_ALT) ? 8 : 0) /* meta key: alt */ + ((client.kb.event.mod & KEY_MOD_CTRL) ? 16 : 0); } if (IS_SET(MODE_MOUSESGR)) { len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", code, x+1, y+1, state ? 'M' : 'm'); } else if (x < 223 && y < 223) { len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", 32+code, 32+x+1, 32+y+1); } else { return; } ttywrite(buf, len, 0); } void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) { client.pointer.position.x = MIN(wintotty(wl_fixed_to_int(x), width), win.tty.width - 1); client.pointer.position.y = MIN(wintotty(wl_fixed_to_int(y), height), win.tty.height - 1); if (IS_SET(MODE_MOUSE) | IS_SET(MODE_MOUSEMOTION) | IS_SET(MODE_MOUSEMANY)) { reportpointer(1); } else if (client.pointer.buttons.button & BTN_LEFT) { selextend( client.pointer.position.x, client.pointer.position.y, SEL_REGULAR, 0 ); win.redraw = 1; } } void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { int snap; client.pointer.buttons.state = state; client.pointer.buttons.button = button; if (IS_SET(MODE_MOUSE) | IS_SET(MODE_MOUSEMANY) | IS_SET(MODE_MOUSEBTN) | IS_SET(MODE_MOUSESGR)) { client.pointer.buttons.button = button; reportpointer(0); return; } if (button == BTN_LEFT && state) { if (client.pointer.buttons.click_combo == 1 && time - client.pointer.buttons.tclick <= doubleclicktimeout) { snap = SNAP_WORD; } else if (client.pointer.buttons.click_combo >= 2 && time - client.pointer.buttons.tclick <= tripleclicktimeout) { snap = SNAP_LINE; } else { client.pointer.buttons.click_combo = 0; snap = 0; } client.pointer.buttons.tclick = time; ++client.pointer.buttons.click_combo; if (IS_SET(MODE_MOUSE) | IS_SET(MODE_MOUSEMANY) | IS_SET(MODE_MOUSEBTN) | IS_SET(MODE_MOUSESGR)) { reportpointer(0); } else { selstart( client.pointer.position.x, client.pointer.position.y, snap ); } win.redraw = 1; } else if (button == BTN_LEFT && !state) { client.pointer.buttons.button &= ~BTN_LEFT; selextend( client.pointer.position.x, client.pointer.position.y, SEL_REGULAR, 1 ); } if (!state) client.pointer.buttons.button = 0; } void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { int v; if (IS_SET(MODE_MOUSE) | IS_SET(MODE_MOUSEMANY) | IS_SET(MODE_MOUSEBTN) | IS_SET(MODE_MOUSESGR) | IS_SET(MODE_MOUSEX10)) { v = wl_fixed_to_int(value); client.pointer.buttons.state = 1; if (v > 0) client.pointer.buttons.button = BTN_MOUSE + 4; else client.pointer.buttons.button = BTN_MOUSE + 3; reportpointer(0); client.pointer.buttons.button = 0; client.pointer.buttons.state = 0; } } void data_device_data_offer(void *data, struct wl_data_device *data_device, struct wl_data_offer *offer) { wl_data_offer_add_listener(offer, &data_offer_listener, NULL); } void data_device_selection(void *data, struct wl_data_device *data_device, struct wl_data_offer *offer) { int fds[2]; ssize_t s; if (offer == 0) return; pipe(fds); wl_data_offer_receive(offer, "text/plain", fds[1]); close(fds[1]); wl_display_roundtrip(client.display); s = read(fds[0], clipboard, CLIPBOARD_SIZE - 1); clipboard[s] = 0; close(fds[0]); wl_data_offer_destroy(offer); } void data_source_send(void *data, struct wl_data_source *source, const char *mime_type, int fd) { if (strcmp(mime_type, "text/plain") == 0) { write(fd, clipboard, strlen(clipboard)); } else { fprintf(stderr, "warning: clipboard request does not support mimetype '%s'", mime_type); } close(fd); } void data_source_cancelled(void *data, struct wl_data_source *source) { wl_data_source_destroy(source); } void setup() { win.mode = MODE_VISIBLE; win.fontcache = create_font_cache(fonts, sizeof(fonts) / sizeof(FontPath), fontsize); selinit(); 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.clipboard.data_device = wl_data_device_manager_get_data_device(client.clipboard.data_device_manager, client.seat); wl_data_device_add_listener(client.clipboard.data_device, &data_device_listener, 0); 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); client.pointer.theme = wl_cursor_theme_load(0, 24, client.shm); client.pointer.cursor = wl_cursor_theme_get_cursor(client.pointer.theme, "text"); client.pointer.image = client.pointer.cursor->images[0]; client.pointer.buffer = wl_cursor_image_get_buffer(client.pointer.image); client.pointer.surface = wl_compositor_create_surface(client.compositor); wl_surface_attach(client.pointer.surface, client.pointer.buffer, 0, 0); wl_surface_commit(client.pointer.surface); wl_display_roundtrip(client.display); draw(); commit_surface(); } void run() { struct itimerspec spec = { 0 }; struct pollfd fds[3]; 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) { win.redraw = 0; if (wl_display_flush(client.display) < 0) { if (errno == EAGAIN) continue; die("wayland error:"); break; } if (poll(fds, sizeof(fds) / sizeof(*fds), -1) < 0) { if (errno == EAGAIN) continue; die("poll error:"); break; } /* handle ttyfd at first so we have the real current screen */ if (fds[2].revents & POLLIN) { ttyread(); win.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); win.redraw = 1; } if (win.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; }