├── .gitignore ├── keymap.h ├── arg.h ├── kbmaptoxkb.c ├── LICENSE ├── Makefile ├── fs.h ├── util.h ├── exportfs.patch ├── util.c ├── server-decoration-protocol.c ├── server-decoration.xml ├── xdg-shell-protocol.c ├── README.md ├── server-decoration-server-protocol.h ├── keymap.c ├── fs.c ├── c9.h ├── c9.c ├── wl9.c └── xdg-shell-server-protocol.h /.gitignore: -------------------------------------------------------------------------------- 1 | /config.mk 2 | /*.o 3 | /wl9 4 | -------------------------------------------------------------------------------- /keymap.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int writekeymap(FILE *f, int (*nextline)(void *, char **, size_t *), void *aux); 5 | uint32_t keymapmod(uint32_t key); 6 | -------------------------------------------------------------------------------- /arg.h: -------------------------------------------------------------------------------- 1 | #define ARGBEGIN \ 2 | for (;;) { \ 3 | if (argc > 0) \ 4 | ++argv, --argc; \ 5 | if (argc == 0 || (*argv)[0] != '-') \ 6 | break; \ 7 | if ((*argv)[1] == '-' && !(*argv)[2]) { \ 8 | ++argv, --argc; \ 9 | break; \ 10 | } \ 11 | for (char *opt_ = &(*argv)[1], done_ = 0; !done_ && *opt_; ++opt_) { \ 12 | switch (*opt_) 13 | 14 | #define ARGEND \ 15 | } \ 16 | } 17 | 18 | #define EARGF(x) \ 19 | (done_ = 1, opt_[1] ? ++opt_ : argv[1] ? --argc, *++argv : ((x), abort(), (char *)0)) 20 | -------------------------------------------------------------------------------- /kbmaptoxkb.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: ISC */ 2 | #define _POSIX_C_SOURCE 200809L 3 | #include "keymap.h" 4 | 5 | static int 6 | nextline(void *aux, char **str, size_t *len) 7 | { 8 | static char *buf; 9 | static size_t buflen; 10 | ssize_t ret; 11 | 12 | ret = getline(&buf, &buflen, stdin); 13 | if (ret < 0) 14 | return ferror(stdin) ? -1 : 0; 15 | if (buf[ret - 1] == '\n') 16 | --ret; 17 | buf[ret] = '\0'; 18 | *str = buf; 19 | *len = ret; 20 | return 1; 21 | } 22 | 23 | int 24 | main(void) 25 | { 26 | return writekeymap(stdout, nextline, NULL) == 0; 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2022 Michael Forney 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose 4 | with or without fee is hereby granted, provided that the above copyright notice 5 | and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 9 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 11 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 12 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 13 | THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS+=-std=c11 -Wall -Wpedantic -Wno-parentheses 2 | CFLAGS+=-D C9_NO_SERVER 3 | LIBS+=-lwayland-server 4 | 5 | -include config.mk 6 | 7 | OBJ=\ 8 | wl9.o\ 9 | c9.o\ 10 | fs.o\ 11 | util.o\ 12 | keymap.o\ 13 | xdg-shell-protocol.o\ 14 | server-decoration-protocol.o\ 15 | 16 | HDR=\ 17 | arg.h\ 18 | c9.h\ 19 | fs.h\ 20 | keymap.h\ 21 | server-decoration-server-protocol.h\ 22 | util.h\ 23 | xdg-shell-client-protocol.h\ 24 | xdg-shell-server-protocol.h\ 25 | 26 | 27 | .PHONY: all 28 | all: wl9 29 | 30 | $(OBJ): $(HDR) 31 | 32 | wl9: $(OBJ) 33 | $(CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) 34 | 35 | kbmaptoxkb: kbmaptoxkb.o keymap.o 36 | $(CC) $(LDFLAGS) -o $@ kbmaptoxkb.o keymap.o 37 | 38 | .PHONY: clean 39 | clean: 40 | rm -f wl9 $(OBJ) kbmaptoxkb kbmaptoxkb.o 41 | -------------------------------------------------------------------------------- /fs.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: ISC */ 2 | #define BUFSIZE (32*1024ul) /* maximum I/O size of virtio-serial */ 3 | #define IOHDRSZ 24 4 | 5 | struct C9aux { 6 | int rfd, wfd; 7 | uint8_t rbuf[BUFSIZE], *rpos, *rend; 8 | uint8_t wbuf[BUFSIZE], *wpos, *wend; 9 | char err[128]; 10 | struct numtab tag; 11 | struct numtab fid; 12 | struct reply *queue; 13 | int ready; 14 | struct callback *cb; 15 | size_t cblen; 16 | struct wl_event_source *idle; 17 | }; 18 | 19 | int fsinit(C9ctx *ctx, C9aux *aux); 20 | void fsasync(C9ctx *ctx, C9tag tag, void (*fn)(C9r *, void *), void *aux); 21 | C9r *fswait(C9ctx *ctx, C9tag tag, C9rtype type); 22 | void fsreadR(C9ctx *ctx); 23 | void fswriteT(C9ctx *ctx); 24 | void fsdispatch(C9ctx *ctx); 25 | 26 | int fsflush(C9ctx *ctx, C9tag oldtag); 27 | int fsattach(C9ctx *ctx, const char *aname); 28 | int fswalk(C9ctx *ctx, C9tag *tagp, int fid, const char *path[]); 29 | int fsopen(C9ctx *ctx, C9tag *tagp, int fid, C9mode mode); 30 | int fsread(C9ctx *ctx, C9tag *tagp, C9r **rp, int fid, uint64_t off, uint32_t len); 31 | int fswrite(C9ctx *ctx, C9tag *tagp, int fid, uint64_t off, const void *buf, uint32_t len); 32 | int fsclunk(C9ctx *ctx, int fid); 33 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: ISC */ 2 | #include 3 | #include 4 | 5 | #define LEN(a) (sizeof (a) / sizeof *(a)) 6 | 7 | struct numtab { 8 | unsigned long *ent; 9 | size_t len; 10 | }; 11 | 12 | int numget(struct numtab *tab); 13 | int numput(struct numtab *tab, int num); 14 | 15 | size_t utf8dec(uint_least32_t *c, const unsigned char *s, size_t n); 16 | 17 | static inline void * 18 | putle16(void *p, unsigned v) 19 | { 20 | unsigned char *b = p; 21 | 22 | b[0] = v & 0xff; 23 | b[1] = v >> 8 & 0xff; 24 | return b + 2; 25 | } 26 | 27 | static inline void * 28 | putle32(void *p, unsigned long v) 29 | { 30 | unsigned char *b = p; 31 | 32 | b[0] = v & 0xff; 33 | b[1] = v >> 8 & 0xff; 34 | b[2] = v >> 16 & 0xff; 35 | b[3] = v >> 24 & 0xff; 36 | return b + 4; 37 | } 38 | 39 | static inline void * 40 | putle64(void *p, unsigned long long v) 41 | { 42 | unsigned char *b = p; 43 | 44 | b[0] = v & 0xff; 45 | b[1] = v >> 8 & 0xff; 46 | b[2] = v >> 16 & 0xff; 47 | b[3] = v >> 24 & 0xff; 48 | b[4] = v >> 32 & 0xff; 49 | b[5] = v >> 40 & 0xff; 50 | b[6] = v >> 48 & 0xff; 51 | b[7] = v >> 56 & 0xff; 52 | return b + 8; 53 | } 54 | 55 | static inline unsigned 56 | getle16(void *p) 57 | { 58 | unsigned char *b = p; 59 | unsigned v; 60 | 61 | v = b[0] & 0xffu; 62 | v |= (b[1] & 0xffu) << 8; 63 | return v; 64 | } 65 | 66 | static inline unsigned long 67 | getle32(void *p) 68 | { 69 | unsigned char *b = p; 70 | unsigned long v; 71 | 72 | v = b[0] & 0xfful; 73 | v |= (b[1] & 0xfful) << 8; 74 | v |= (b[2] & 0xfful) << 16; 75 | v |= (b[3] & 0xfful) << 24; 76 | return v; 77 | } 78 | -------------------------------------------------------------------------------- /exportfs.patch: -------------------------------------------------------------------------------- 1 | From 6879e421468965c264f65f170fe176349bd8b64b Mon Sep 17 00:00:00 2001 2 | From: Michael Forney 3 | Date: Wed, 26 Oct 2022 16:41:23 -0700 4 | Subject: [PATCH] exportfs: allow attaching to fids using special aname 5 | 6 | --- 7 | sys/src/cmd/exportfs/exportsrv.c | 24 ++++++++++++++++++++---- 8 | 1 file changed, 20 insertions(+), 4 deletions(-) 9 | 10 | diff --git a/sys/src/cmd/exportfs/exportsrv.c b/sys/src/cmd/exportfs/exportsrv.c 11 | index 3bd9e3aeb..b49a3964a 100644 12 | --- a/sys/src/cmd/exportfs/exportsrv.c 13 | +++ b/sys/src/cmd/exportfs/exportsrv.c 14 | @@ -84,8 +84,8 @@ Xattach(Fsrpc *t) 15 | { 16 | int i, nfd; 17 | Fcall rhdr; 18 | - Fid *f; 19 | - char buf[128]; 20 | + Fid *f, *sf; 21 | + char buf[128], *path; 22 | 23 | f = newfid(t->work.fid); 24 | if(f == nil) { 25 | @@ -94,7 +94,7 @@ Xattach(Fsrpc *t) 26 | return; 27 | } 28 | 29 | - if(srvfd >= 0){ 30 | + if(srvfd >= 0 || *t->work.aname){ 31 | if(psmpt == nil){ 32 | Nomount: 33 | reply(&t->work, &rhdr, Enopsmt); 34 | @@ -112,7 +112,23 @@ Xattach(Fsrpc *t) 35 | if(f->f == nil) 36 | goto Nomount; 37 | sprint(buf, "/mnt/exportfs/%d", i); 38 | - nfd = dup(srvfd, -1); 39 | + if(srvfd >= 0) 40 | + nfd = dup(srvfd, -1); 41 | + else{ 42 | + if(strlen(t->work.aname) < 12) 43 | + goto Nomount; 44 | + sf = getfid(atoi(t->work.aname)); 45 | + if(sf == nil) { 46 | + reply(&t->work, &rhdr, Ebadfid); 47 | + freefid(t->work.fid); 48 | + putsbuf(t); 49 | + return; 50 | + } 51 | + path = makepath(sf->f, ""); 52 | + nfd = open(path, ORDWR); 53 | + free(path); 54 | + t->work.aname += 12; 55 | + } 56 | if(amount(nfd, buf, MREPL|MCREATE, t->work.aname) == -1){ 57 | errstr(buf, sizeof buf); 58 | reply(&t->work, &rhdr, buf); 59 | -- 60 | 2.37.3 61 | 62 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: ISC */ 2 | #include 3 | #include 4 | #include 5 | #include "util.h" 6 | 7 | #define ENTBIT (sizeof *((struct numtab *)0)->ent * CHAR_BIT) 8 | 9 | int 10 | numget(struct numtab *tab) 11 | { 12 | unsigned long *ent, mask; 13 | int num; 14 | 15 | for (ent = tab->ent; ent < tab->ent + tab->len; ent++) { 16 | if (*ent) { 17 | num = (ent - tab->ent) * ENTBIT; 18 | for (mask = *ent; (mask & 1) == 0; mask >>= 1) 19 | ++num; 20 | *ent &= ~(1ul << num); 21 | return num; 22 | } 23 | } 24 | ent = realloc(tab->ent, (tab->len + 1) * sizeof *ent); 25 | if (!ent) 26 | return -1; 27 | tab->ent = ent; 28 | ent += tab->len; 29 | *ent = -2ul; 30 | num = tab->len * ENTBIT; 31 | ++tab->len; 32 | return num; 33 | } 34 | 35 | int 36 | numput(struct numtab *tab, int num) 37 | { 38 | size_t index; 39 | unsigned long mask; 40 | 41 | if (num < 0) 42 | return -1; 43 | index = num / ENTBIT; 44 | mask = 1ul << num % ENTBIT; 45 | if (index >= tab->len || tab->ent[index] & mask) 46 | return -1; 47 | tab->ent[index] |= mask; 48 | return 0; 49 | } 50 | 51 | size_t 52 | utf8dec(uint_least32_t *c, const unsigned char *s, size_t n) 53 | { 54 | size_t i, l; 55 | unsigned char b; 56 | uint_least32_t x; 57 | 58 | b = s[0]; 59 | if (b < 0x80) { 60 | *c = b; 61 | return 1; 62 | } 63 | if ((b & 0xe0) == 0xc0) { 64 | x = b & 0x1f; 65 | l = 2; 66 | } else if ((b & 0xf0) == 0xe0) { 67 | x = b & 0x0f; 68 | l = 3; 69 | } else if ((b & 0xf8) == 0xf0) { 70 | x = b & 0x07; 71 | l = 4; 72 | } else { 73 | return -1; 74 | } 75 | if (n < l) 76 | return -1; 77 | for (i = 1; i < l; ++i) { 78 | b = *++s; 79 | if ((b & 0xc0) != 0x80) 80 | return -1; 81 | x = x << 6 | b & 0x3f; 82 | } 83 | if (x >= 0x110000 || x - 0xd800 < 0x0200) 84 | return -1; 85 | *c = x; 86 | return l; 87 | } 88 | -------------------------------------------------------------------------------- /server-decoration-protocol.c: -------------------------------------------------------------------------------- 1 | /* Generated by wayland-scanner 1.20.0 */ 2 | 3 | /* 4 | * SPDX-FileCopyrightText: 2015 Martin Gräßlin 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-or-later 7 | */ 8 | 9 | #include 10 | #include 11 | #include "wayland-util.h" 12 | 13 | #ifndef __has_attribute 14 | # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ 15 | #endif 16 | 17 | #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) 18 | #define WL_PRIVATE __attribute__ ((visibility("hidden"))) 19 | #else 20 | #define WL_PRIVATE 21 | #endif 22 | 23 | extern const struct wl_interface org_kde_kwin_server_decoration_interface; 24 | extern const struct wl_interface wl_surface_interface; 25 | 26 | static const struct wl_interface *server_decoration_types[] = { 27 | NULL, 28 | &org_kde_kwin_server_decoration_interface, 29 | &wl_surface_interface, 30 | }; 31 | 32 | static const struct wl_message org_kde_kwin_server_decoration_manager_requests[] = { 33 | { "create", "no", server_decoration_types + 1 }, 34 | }; 35 | 36 | static const struct wl_message org_kde_kwin_server_decoration_manager_events[] = { 37 | { "default_mode", "u", server_decoration_types + 0 }, 38 | }; 39 | 40 | WL_PRIVATE const struct wl_interface org_kde_kwin_server_decoration_manager_interface = { 41 | "org_kde_kwin_server_decoration_manager", 1, 42 | 1, org_kde_kwin_server_decoration_manager_requests, 43 | 1, org_kde_kwin_server_decoration_manager_events, 44 | }; 45 | 46 | static const struct wl_message org_kde_kwin_server_decoration_requests[] = { 47 | { "release", "", server_decoration_types + 0 }, 48 | { "request_mode", "u", server_decoration_types + 0 }, 49 | }; 50 | 51 | static const struct wl_message org_kde_kwin_server_decoration_events[] = { 52 | { "mode", "u", server_decoration_types + 0 }, 53 | }; 54 | 55 | WL_PRIVATE const struct wl_interface org_kde_kwin_server_decoration_interface = { 56 | "org_kde_kwin_server_decoration", 1, 57 | 2, org_kde_kwin_server_decoration_requests, 58 | 1, org_kde_kwin_server_decoration_events, 59 | }; 60 | 61 | -------------------------------------------------------------------------------- /server-decoration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | This interface allows to coordinate whether the server should create 11 | a server-side window decoration around a wl_surface representing a 12 | shell surface (wl_shell_surface or similar). By announcing support 13 | for this interface the server indicates that it supports server 14 | side decorations. 15 | 16 | Use in conjunction with zxdg_decoration_manager_v1 is undefined. 17 | 18 | 19 | 20 | When a client creates a server-side decoration object it indicates 21 | that it supports the protocol. The client is supposed to tell the 22 | server whether it wants server-side decorations or will provide 23 | client-side decorations. 24 | 25 | If the client does not create a server-side decoration object for 26 | a surface the server interprets this as lack of support for this 27 | protocol and considers it as client-side decorated. Nevertheless a 28 | client-side decorated surface should use this protocol to indicate 29 | to the server that it does not want a server-side deco. 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | This event is emitted directly after binding the interface. It contains 43 | the default mode for the decoration. When a new server decoration object 44 | is created this new object will be in the default mode until the first 45 | request_mode is requested. 46 | 47 | The server may change the default mode at any time. 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | This event is emitted directly after the decoration is created and 69 | represents the base decoration policy by the server. E.g. a server 70 | which wants all surfaces to be client-side decorated will send Client, 71 | a server which wants server-side decoration will send Server. 72 | 73 | The client can request a different mode through the decoration request. 74 | The server will acknowledge this by another event with the same mode. So 75 | even if a server prefers server-side decoration it's possible to force a 76 | client-side decoration. 77 | 78 | The server may emit this event at any time. In this case the client can 79 | again request a different mode. It's the responsibility of the server to 80 | prevent a feedback loop. 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /xdg-shell-protocol.c: -------------------------------------------------------------------------------- 1 | /* Generated by wayland-scanner 1.20.0 */ 2 | 3 | /* 4 | * Copyright © 2008-2013 Kristian Høgsberg 5 | * Copyright © 2013 Rafael Antognolli 6 | * Copyright © 2013 Jasper St. Pierre 7 | * Copyright © 2010-2013 Intel Corporation 8 | * Copyright © 2015-2017 Samsung Electronics Co., Ltd 9 | * Copyright © 2015-2017 Red Hat Inc. 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining a 12 | * copy of this software and associated documentation files (the "Software"), 13 | * to deal in the Software without restriction, including without limitation 14 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 15 | * and/or sell copies of the Software, and to permit persons to whom the 16 | * Software is furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice (including the next 19 | * paragraph) shall be included in all copies or substantial portions of the 20 | * Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 25 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | * DEALINGS IN THE SOFTWARE. 29 | */ 30 | 31 | #include 32 | #include 33 | #include "wayland-util.h" 34 | 35 | extern const struct wl_interface wl_output_interface; 36 | extern const struct wl_interface wl_seat_interface; 37 | extern const struct wl_interface wl_surface_interface; 38 | extern const struct wl_interface xdg_popup_interface; 39 | extern const struct wl_interface xdg_positioner_interface; 40 | extern const struct wl_interface xdg_surface_interface; 41 | extern const struct wl_interface xdg_toplevel_interface; 42 | 43 | static const struct wl_interface *xdg_shell_types[] = { 44 | NULL, 45 | NULL, 46 | NULL, 47 | NULL, 48 | &xdg_positioner_interface, 49 | &xdg_surface_interface, 50 | &wl_surface_interface, 51 | &xdg_toplevel_interface, 52 | &xdg_popup_interface, 53 | &xdg_surface_interface, 54 | &xdg_positioner_interface, 55 | &xdg_toplevel_interface, 56 | &wl_seat_interface, 57 | NULL, 58 | NULL, 59 | NULL, 60 | &wl_seat_interface, 61 | NULL, 62 | &wl_seat_interface, 63 | NULL, 64 | NULL, 65 | &wl_output_interface, 66 | &wl_seat_interface, 67 | NULL, 68 | &xdg_positioner_interface, 69 | NULL, 70 | }; 71 | 72 | static const struct wl_message xdg_wm_base_requests[] = { 73 | { "destroy", "", xdg_shell_types + 0 }, 74 | { "create_positioner", "n", xdg_shell_types + 4 }, 75 | { "get_xdg_surface", "no", xdg_shell_types + 5 }, 76 | { "pong", "u", xdg_shell_types + 0 }, 77 | }; 78 | 79 | static const struct wl_message xdg_wm_base_events[] = { 80 | { "ping", "u", xdg_shell_types + 0 }, 81 | }; 82 | 83 | WL_EXPORT const struct wl_interface xdg_wm_base_interface = { 84 | "xdg_wm_base", 3, 85 | 4, xdg_wm_base_requests, 86 | 1, xdg_wm_base_events, 87 | }; 88 | 89 | static const struct wl_message xdg_positioner_requests[] = { 90 | { "destroy", "", xdg_shell_types + 0 }, 91 | { "set_size", "ii", xdg_shell_types + 0 }, 92 | { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, 93 | { "set_anchor", "u", xdg_shell_types + 0 }, 94 | { "set_gravity", "u", xdg_shell_types + 0 }, 95 | { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, 96 | { "set_offset", "ii", xdg_shell_types + 0 }, 97 | { "set_reactive", "3", xdg_shell_types + 0 }, 98 | { "set_parent_size", "3ii", xdg_shell_types + 0 }, 99 | { "set_parent_configure", "3u", xdg_shell_types + 0 }, 100 | }; 101 | 102 | WL_EXPORT const struct wl_interface xdg_positioner_interface = { 103 | "xdg_positioner", 3, 104 | 10, xdg_positioner_requests, 105 | 0, NULL, 106 | }; 107 | 108 | static const struct wl_message xdg_surface_requests[] = { 109 | { "destroy", "", xdg_shell_types + 0 }, 110 | { "get_toplevel", "n", xdg_shell_types + 7 }, 111 | { "get_popup", "n?oo", xdg_shell_types + 8 }, 112 | { "set_window_geometry", "iiii", xdg_shell_types + 0 }, 113 | { "ack_configure", "u", xdg_shell_types + 0 }, 114 | }; 115 | 116 | static const struct wl_message xdg_surface_events[] = { 117 | { "configure", "u", xdg_shell_types + 0 }, 118 | }; 119 | 120 | WL_EXPORT const struct wl_interface xdg_surface_interface = { 121 | "xdg_surface", 3, 122 | 5, xdg_surface_requests, 123 | 1, xdg_surface_events, 124 | }; 125 | 126 | static const struct wl_message xdg_toplevel_requests[] = { 127 | { "destroy", "", xdg_shell_types + 0 }, 128 | { "set_parent", "?o", xdg_shell_types + 11 }, 129 | { "set_title", "s", xdg_shell_types + 0 }, 130 | { "set_app_id", "s", xdg_shell_types + 0 }, 131 | { "show_window_menu", "ouii", xdg_shell_types + 12 }, 132 | { "move", "ou", xdg_shell_types + 16 }, 133 | { "resize", "ouu", xdg_shell_types + 18 }, 134 | { "set_max_size", "ii", xdg_shell_types + 0 }, 135 | { "set_min_size", "ii", xdg_shell_types + 0 }, 136 | { "set_maximized", "", xdg_shell_types + 0 }, 137 | { "unset_maximized", "", xdg_shell_types + 0 }, 138 | { "set_fullscreen", "?o", xdg_shell_types + 21 }, 139 | { "unset_fullscreen", "", xdg_shell_types + 0 }, 140 | { "set_minimized", "", xdg_shell_types + 0 }, 141 | }; 142 | 143 | static const struct wl_message xdg_toplevel_events[] = { 144 | { "configure", "iia", xdg_shell_types + 0 }, 145 | { "close", "", xdg_shell_types + 0 }, 146 | }; 147 | 148 | WL_EXPORT const struct wl_interface xdg_toplevel_interface = { 149 | "xdg_toplevel", 3, 150 | 14, xdg_toplevel_requests, 151 | 2, xdg_toplevel_events, 152 | }; 153 | 154 | static const struct wl_message xdg_popup_requests[] = { 155 | { "destroy", "", xdg_shell_types + 0 }, 156 | { "grab", "ou", xdg_shell_types + 22 }, 157 | { "reposition", "3ou", xdg_shell_types + 24 }, 158 | }; 159 | 160 | static const struct wl_message xdg_popup_events[] = { 161 | { "configure", "iiii", xdg_shell_types + 0 }, 162 | { "popup_done", "", xdg_shell_types + 0 }, 163 | { "repositioned", "3u", xdg_shell_types + 0 }, 164 | }; 165 | 166 | WL_EXPORT const struct wl_interface xdg_popup_interface = { 167 | "xdg_popup", 3, 168 | 3, xdg_popup_requests, 169 | 3, xdg_popup_events, 170 | }; 171 | 172 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wl9 2 | 3 | wl9 is a POSIX wayland server that presents its windows to Plan 9's 4 | rio. 5 | 6 | It communicates over 9p to an `exportfs` instance running host Plan 7 | 9 system through a pair of file descriptors (one for reading, and 8 | one for writing). 9 | 10 | A lightly patched version of sigrid's [c9] library is used as a 9p 11 | client. 12 | 13 | [c9]: http://shithub.us/sigrid/c9/HEAD/info.html 14 | 15 | ## Usage 16 | 17 | ``` 18 | wl9 [-t rfd[,wfd]] [cmd [args...]] 19 | ``` 20 | 21 | The `-t` option specifies the file descriptors for the 9p connection. 22 | They should be set up in advance before running wl9, or if not 23 | specified, `/dev/virtio-ports/term` is opened for reading and 24 | writing. 25 | 26 | If `cmd [args...]` is given, it is launched as a child process after 27 | wl9 sets up its sockets. The first window created by the child 28 | will run in the existing `/mnt/wsys` instead of mounting `$wsys`. 29 | This has the effect of replacing the window running `exportfs`. 30 | Additionally, wl9 will stop automatically when it has no clients. 31 | 32 | ## Examples 33 | 34 | ### vmx 35 | 36 | You can connect through a virtio-serial pipe from a unix guest to 37 | a host 9front system. 38 | 39 | ``` 40 | vmx -c virtio:name:term!^<>{exportfs -r / -m 32768 >[1=0]} 41 | ``` 42 | 43 | If the guest system has hotplug rules to create named symlinks for 44 | virtio pipes, you can just run `wl9`. Otherwise, you will need to 45 | setup the appropriate redirections. 46 | 47 | ``` 48 | wl9 -t 0 <>/dev/vportNpM 49 | ``` 50 | 51 | ### ssh 52 | 53 | You can use a pipeline with `exportfs` and `ssh` to remotely run a 54 | wayland application on a unix server that has wl9 installed. 55 | 56 | ``` 57 | exportf -r / <[0=1] | ssh host wl9 -t 0,1 cmd >[1=0] 58 | ``` 59 | 60 | ## Wsys 61 | 62 | rio windows can be created in two ways: by writing a `new` message 63 | to `/dev/wctl`, or by attaching to `$wsys` with a `new` message as 64 | the `aname`. Unfortunately, with the first method we can't find the 65 | `winid` of the created window, so wl9 uses the second method. 66 | 67 | However, attaching to `$wsys` poses an additional problem: we either 68 | need a separate 9p channel for `exportfs -S $wsys`, or we need to 69 | speak 9p over 9p to a separate `exportfs` process spawned by the 70 | original. 71 | 72 | At least for now, wl9 uses a third approach. Using a small [exportfs 73 | patch], we can attach to services by walking to their fid, and 74 | attaching with a 12 character aname prefix consisting of an 11 75 | character decimal fid number, and a blank. The prefix is stripped 76 | for the mount of the service. 77 | 78 | [exportfs patch]: https://git.sr.ht/~mcf/wl9/blob/main/exportfs.patch 79 | 80 | Once we mount `$wsys`, wl9 opens several files: 81 | 82 | - `winname`: used to locate the draw(3) image for the window 83 | - `label`: `xdg_toplevel.set_title` requests are translated to 84 | writes to `label` 85 | - `wctl`: used to monitor window coordinates and status 86 | - `mouse`: used to read mouse events 87 | - `kbd`: used to read keyboard events 88 | 89 | ## Draw 90 | 91 | ## Snarf 92 | 93 | Still kind of buggy with some applications. 94 | 95 | ## Mouse 96 | 97 | ### Cursor 98 | 99 | Not implemented yet. 100 | 101 | ## Keyboard 102 | 103 | ### Keyboard map 104 | 105 | For key codes, we use the 'k' and 'K' events from `/dev/kbd`. These 106 | codes are the unshifted unicode characters corresponding to the 107 | keys pressed and released. If there is no unshifted character 108 | corresponding to that scancode, or the scancode is escaped, the 109 | shifted character is used instead. 110 | 111 | In typical keymaps, some keys are mapped to same unshifted character, 112 | i.e. the mapping from scancode to character is not 1-1. Unfortunately, 113 | This means that we can't distinguish which of the keys were pressed. 114 | If there is a conflict, we choose the first one we encounter. 115 | 116 | In particular, for the default kbmap, we have 117 | 118 | | rune | key (scan) ... | 119 | | --- | --- | 120 | | \n | ENTER (`1c`) KPENTER (`e0 1c`) | 121 | | - | MINUS (`0c`) KPMINUS (`4a`) | 122 | | . | DOT (`34`) KPDOT (`53`) | 123 | | / | SLASH (`35`) KPSLASH (`e0 35`) | 124 | | 0 | 0 (`0b`) KP0 (`52`) | 125 | | 1 | 1 (`02`) KP1 (`4f`) | 126 | | 2 | 2 (`03`) KP2 (`50`) | 127 | | 3 | 3 (`04`) KP3 (`51`) | 128 | | 4 | 4 (`05`) KP4 (`4b`) | 129 | | 5 | 5 (`06`) KP5 (`4c`) | 130 | | 6 | 6 (`07`) KP6 (`4d`) | 131 | | 7 | 7 (`08`) KP7 (`47`) | 132 | | 8 | 8 (`09`) KP8 (`48`) | 133 | | 9 | 9 (`0a`) KP9 (`49`) | 134 | | Kup | UP (`e0 48`) ? (`7b`) ? (`e0 79`) | 135 | | Kshift | LEFTSHIFT (`2a`) RIGHTSHIFT (`36`) | 136 | | Kctl | LEFTCTRL (`1d`) RIGHTCTRL (`e0 1d`) CAPSLOCK (`3a`) | 137 | | Kdown | DOWN (`e0 50`) ? (`79`) | 138 | 139 | At startup, /dev/kbmap is translated to an XKB keymap as follows: 140 | 141 | For every keymap entry with table 0 (unshifted) 2 (escaped unshifted), 142 | 5 (escaped with control), or 6 (escaped with shift), we add a keycode 143 | `` to the `xkb_keycodes` section with value `0xXXXX + 8`, 144 | where `U+XXXX` is the value of mapped unicode character. The 8 145 | offset is due to historical reasons to translate an kernel event 146 | code to an XKB keycode. 147 | 148 | We use the standard 2-level `xkb_types` (excluding `KEYPAD` since 149 | we can't uniquely identify those keys). Currently, tables 3-9 in 150 | `/dev/kbmap` are ignored, which means that shift only affects 151 | non-escaped keycodes, and control, alt, and mod4. 152 | 153 | ``` 154 | xkb_types "plan9" { 155 | type "ONE_LEVEL" { 156 | modifiers = none; 157 | level_name[1] = "Any"; 158 | }; 159 | type "TWO_LEVEL" { 160 | modifiers = Shift; 161 | map[Shift] = 2; 162 | level_name[1] = "Base"; 163 | level_name[2] = "Shift"; 164 | }; 165 | type "ALPHABETIC" { 166 | modifiers = Shift+Lock; 167 | map[Shift] = 2; 168 | map[Lock] = 2; 169 | level_name[1] = "Base"; 170 | level_name[2] = "Caps"; 171 | }; 172 | }; 173 | ``` 174 | 175 | We use the following `xkb_compat` rules for standard shift/caps 176 | lock handling. 177 | 178 | ``` 179 | xkb_compat "plan9" { 180 | interpret Shift_L { 181 | action = SetMods(modifiers=Shift,clearLocks); 182 | }; 183 | interpret Caps_Lock { 184 | action = LockMods(modifiers=Lock); 185 | }; 186 | }; 187 | ``` 188 | 189 | The `xkb_symbols` section is constructed with a key for each keycode. 190 | Since XKB keysyms are more granular than Plan 9 key characters, for 191 | the ambiguous cases, we choose keysyms corresponding to first key 192 | in the table above. Additionally, though Plan 9 doesn't have a 193 | keymap entry the right meta key (bug?), `Kmod4` is mapped to `Super_L` 194 | rather than `Super_R`. 195 | 196 | If that keycode corresponds to an non-escaped scancode and that 197 | scancode has an entry in table 1 (shift) as well, we add a two-level 198 | key, `key {[KEY0, KEY1]}`, where `KEY0` and `KEY1` are the 199 | names of the XKB symbols we mapped the table 0 and table 1 characters 200 | to. Otherwise, we add a one-level key f`key {[KEY02]}`. 201 | If there is no XKB name for the character, we use an XKB unicode 202 | keysym. 203 | -------------------------------------------------------------------------------- /server-decoration-server-protocol.h: -------------------------------------------------------------------------------- 1 | /* Generated by wayland-scanner 1.20.0 */ 2 | 3 | #ifndef SERVER_DECORATION_SERVER_PROTOCOL_H 4 | #define SERVER_DECORATION_SERVER_PROTOCOL_H 5 | 6 | #include 7 | #include 8 | #include "wayland-server.h" 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | struct wl_client; 15 | struct wl_resource; 16 | 17 | /** 18 | * @page page_server_decoration The server_decoration protocol 19 | * @section page_ifaces_server_decoration Interfaces 20 | * - @subpage page_iface_org_kde_kwin_server_decoration_manager - Server side window decoration manager 21 | * - @subpage page_iface_org_kde_kwin_server_decoration - 22 | * @section page_copyright_server_decoration Copyright 23 | *
 24 |  *
 25 |  * SPDX-FileCopyrightText: 2015 Martin Gräßlin
 26 |  *
 27 |  * SPDX-License-Identifier: LGPL-2.1-or-later
 28 |  * 
29 | */ 30 | struct org_kde_kwin_server_decoration; 31 | struct org_kde_kwin_server_decoration_manager; 32 | struct wl_surface; 33 | 34 | #ifndef ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_INTERFACE 35 | #define ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_INTERFACE 36 | /** 37 | * @page page_iface_org_kde_kwin_server_decoration_manager org_kde_kwin_server_decoration_manager 38 | * @section page_iface_org_kde_kwin_server_decoration_manager_desc Description 39 | * 40 | * This interface allows to coordinate whether the server should create 41 | * a server-side window decoration around a wl_surface representing a 42 | * shell surface (wl_shell_surface or similar). By announcing support 43 | * for this interface the server indicates that it supports server 44 | * side decorations. 45 | * 46 | * Use in conjunction with zxdg_decoration_manager_v1 is undefined. 47 | * @section page_iface_org_kde_kwin_server_decoration_manager_api API 48 | * See @ref iface_org_kde_kwin_server_decoration_manager. 49 | */ 50 | /** 51 | * @defgroup iface_org_kde_kwin_server_decoration_manager The org_kde_kwin_server_decoration_manager interface 52 | * 53 | * This interface allows to coordinate whether the server should create 54 | * a server-side window decoration around a wl_surface representing a 55 | * shell surface (wl_shell_surface or similar). By announcing support 56 | * for this interface the server indicates that it supports server 57 | * side decorations. 58 | * 59 | * Use in conjunction with zxdg_decoration_manager_v1 is undefined. 60 | */ 61 | extern const struct wl_interface org_kde_kwin_server_decoration_manager_interface; 62 | #endif 63 | #ifndef ORG_KDE_KWIN_SERVER_DECORATION_INTERFACE 64 | #define ORG_KDE_KWIN_SERVER_DECORATION_INTERFACE 65 | /** 66 | * @page page_iface_org_kde_kwin_server_decoration org_kde_kwin_server_decoration 67 | * @section page_iface_org_kde_kwin_server_decoration_api API 68 | * See @ref iface_org_kde_kwin_server_decoration. 69 | */ 70 | /** 71 | * @defgroup iface_org_kde_kwin_server_decoration The org_kde_kwin_server_decoration interface 72 | */ 73 | extern const struct wl_interface org_kde_kwin_server_decoration_interface; 74 | #endif 75 | 76 | #ifndef ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_ENUM 77 | #define ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_ENUM 78 | /** 79 | * @ingroup iface_org_kde_kwin_server_decoration_manager 80 | * Possible values to use in request_mode and the event mode. 81 | */ 82 | enum org_kde_kwin_server_decoration_manager_mode { 83 | /** 84 | * Undecorated: The surface is not decorated at all, neither server nor client-side. An example is a popup surface which should not be decorated. 85 | */ 86 | ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_NONE = 0, 87 | /** 88 | * Client-side decoration: The decoration is part of the surface and the client. 89 | */ 90 | ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_CLIENT = 1, 91 | /** 92 | * Server-side decoration: The server embeds the surface into a decoration frame. 93 | */ 94 | ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER = 2, 95 | }; 96 | #endif /* ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_ENUM */ 97 | 98 | /** 99 | * @ingroup iface_org_kde_kwin_server_decoration_manager 100 | * @struct org_kde_kwin_server_decoration_manager_interface 101 | */ 102 | struct org_kde_kwin_server_decoration_manager_interface { 103 | /** 104 | * Create a server-side decoration object for a given surface 105 | * 106 | * When a client creates a server-side decoration object it 107 | * indicates that it supports the protocol. The client is supposed 108 | * to tell the server whether it wants server-side decorations or 109 | * will provide client-side decorations. 110 | * 111 | * If the client does not create a server-side decoration object 112 | * for a surface the server interprets this as lack of support for 113 | * this protocol and considers it as client-side decorated. 114 | * Nevertheless a client-side decorated surface should use this 115 | * protocol to indicate to the server that it does not want a 116 | * server-side deco. 117 | */ 118 | void (*create)(struct wl_client *client, 119 | struct wl_resource *resource, 120 | uint32_t id, 121 | struct wl_resource *surface); 122 | }; 123 | 124 | #define ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_DEFAULT_MODE 0 125 | 126 | /** 127 | * @ingroup iface_org_kde_kwin_server_decoration_manager 128 | */ 129 | #define ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_DEFAULT_MODE_SINCE_VERSION 1 130 | 131 | /** 132 | * @ingroup iface_org_kde_kwin_server_decoration_manager 133 | */ 134 | #define ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_CREATE_SINCE_VERSION 1 135 | 136 | /** 137 | * @ingroup iface_org_kde_kwin_server_decoration_manager 138 | * Sends an default_mode event to the client owning the resource. 139 | * @param resource_ The client's resource 140 | * @param mode The default decoration mode applied to newly created server decorations. 141 | */ 142 | static inline void 143 | org_kde_kwin_server_decoration_manager_send_default_mode(struct wl_resource *resource_, uint32_t mode) 144 | { 145 | wl_resource_post_event(resource_, ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_DEFAULT_MODE, mode); 146 | } 147 | 148 | #ifndef ORG_KDE_KWIN_SERVER_DECORATION_MODE_ENUM 149 | #define ORG_KDE_KWIN_SERVER_DECORATION_MODE_ENUM 150 | /** 151 | * @ingroup iface_org_kde_kwin_server_decoration 152 | * Possible values to use in request_mode and the event mode. 153 | */ 154 | enum org_kde_kwin_server_decoration_mode { 155 | /** 156 | * Undecorated: The surface is not decorated at all, neither server nor client-side. An example is a popup surface which should not be decorated. 157 | */ 158 | ORG_KDE_KWIN_SERVER_DECORATION_MODE_NONE = 0, 159 | /** 160 | * Client-side decoration: The decoration is part of the surface and the client. 161 | */ 162 | ORG_KDE_KWIN_SERVER_DECORATION_MODE_CLIENT = 1, 163 | /** 164 | * Server-side decoration: The server embeds the surface into a decoration frame. 165 | */ 166 | ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER = 2, 167 | }; 168 | #endif /* ORG_KDE_KWIN_SERVER_DECORATION_MODE_ENUM */ 169 | 170 | /** 171 | * @ingroup iface_org_kde_kwin_server_decoration 172 | * @struct org_kde_kwin_server_decoration_interface 173 | */ 174 | struct org_kde_kwin_server_decoration_interface { 175 | /** 176 | * release the server decoration object 177 | * 178 | * 179 | */ 180 | void (*release)(struct wl_client *client, 181 | struct wl_resource *resource); 182 | /** 183 | * The decoration mode the surface wants to use. 184 | * 185 | * 186 | * @param mode The mode this surface wants to use. 187 | */ 188 | void (*request_mode)(struct wl_client *client, 189 | struct wl_resource *resource, 190 | uint32_t mode); 191 | }; 192 | 193 | #define ORG_KDE_KWIN_SERVER_DECORATION_MODE 0 194 | 195 | /** 196 | * @ingroup iface_org_kde_kwin_server_decoration 197 | */ 198 | #define ORG_KDE_KWIN_SERVER_DECORATION_MODE_SINCE_VERSION 1 199 | 200 | /** 201 | * @ingroup iface_org_kde_kwin_server_decoration 202 | */ 203 | #define ORG_KDE_KWIN_SERVER_DECORATION_RELEASE_SINCE_VERSION 1 204 | /** 205 | * @ingroup iface_org_kde_kwin_server_decoration 206 | */ 207 | #define ORG_KDE_KWIN_SERVER_DECORATION_REQUEST_MODE_SINCE_VERSION 1 208 | 209 | /** 210 | * @ingroup iface_org_kde_kwin_server_decoration 211 | * Sends an mode event to the client owning the resource. 212 | * @param resource_ The client's resource 213 | * @param mode The decoration mode applied to the surface by the server. 214 | */ 215 | static inline void 216 | org_kde_kwin_server_decoration_send_mode(struct wl_resource *resource_, uint32_t mode) 217 | { 218 | wl_resource_post_event(resource_, ORG_KDE_KWIN_SERVER_DECORATION_MODE, mode); 219 | } 220 | 221 | #ifdef __cplusplus 222 | } 223 | #endif 224 | 225 | #endif 226 | -------------------------------------------------------------------------------- /keymap.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: ISC */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define LEN(a) ((sizeof a) / (sizeof *a)) 10 | 11 | static const char xkbnames[] = 12 | "\0" 13 | "BackSpace\0" 14 | "Tab\0" 15 | "Return\0" 16 | "Escape\0" 17 | "space\0" 18 | "exclam\0" 19 | "quotedbl\0" 20 | "numbersign\0" 21 | "dollar\0" 22 | "percent\0" 23 | "ampersand\0" 24 | "apostrophe\0" 25 | "parenleft\0" 26 | "parenright\0" 27 | "asterisk\0" 28 | "plus\0" 29 | "comma\0" 30 | "minus\0" 31 | "period\0" 32 | "slash\0" 33 | "0\0001\0002\0003\0004\0005\0006\0007\0008\0009\0" 34 | "colon\0" 35 | "semicolon\0" 36 | "less\0" 37 | "equal\0" 38 | "greater\0" 39 | "question\0" 40 | "at\0" 41 | "A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M\0" 42 | "N\0O\0P\0Q\0R\0S\0T\0U\0V\0W\0X\0Y\0Z\0" 43 | "bracketleft\0" 44 | "backslash\0" 45 | "bracketright\0" 46 | "asciicircum\0" 47 | "underscore\0" 48 | "grave\0" 49 | "a\0b\0c\0d\0e\0f\0g\0h\0i\0j\0k\0l\0m\0" 50 | "n\0o\0p\0q\0r\0s\0t\0u\0v\0w\0x\0y\0z\0" 51 | "braceleft\0" 52 | "bar\0" 53 | "braceright\0" 54 | "asciitilde\0" 55 | "Delete\0" 56 | "F1\0F2\0F3\0F4\0F5\0F6\0F7\0F8\0F9\0F10\0F11\0F12\0" 57 | "Home\0" 58 | "Up\0" 59 | "Prior\0" 60 | "Print\0" 61 | "Left\0" 62 | "Right\0" 63 | "Next\0" 64 | "Insert\0" 65 | "Alt_L\0" 66 | "Shift_L\0" 67 | "Control_L\0" 68 | "End\0" 69 | "Scroll_Lock\0" 70 | "XF86AudioPrev\0" 71 | "XF86AudioNext\0" 72 | "XF86AudioPlay\0" 73 | "XF86AudioLowerVolume\0" 74 | "XF86AudioRaiseVolume\0" 75 | "XF86AudioMute\0" 76 | "XF86MonBrightnessDown\0" 77 | "XF86MonBrightnessUp\0" 78 | "Down\0" 79 | "Break\0" 80 | "Caps_Lock\0" 81 | "Num_Lock\0" 82 | "Alt_R\0" 83 | "Super_L"; 84 | 85 | static const uint_least16_t xkbascii[128] = { 86 | 0, 0, 0, 0, 0, 0, 0, 0, 87 | 1, 11, 15, 0, 0, 0, 0, 0, 88 | 0, 0, 0, 0, 0, 0, 0, 0, 89 | 0, 0, 0, 22, 0, 0, 0, 0, 90 | 29, 35, 42, 51, 62, 69, 77, 87, 91 | 98, 108, 119, 128, 133, 139, 145, 152, 92 | 158, 160, 162, 164, 166, 168, 170, 172, 93 | 174, 176, 178, 184, 194, 199, 205, 213, 94 | 222, 225, 227, 229, 231, 233, 235, 237, 95 | 239, 241, 243, 245, 247, 249, 251, 253, 96 | 255, 257, 259, 261, 263, 265, 267, 269, 97 | 271, 273, 275, 277, 289, 299, 312, 324, 98 | 335, 341, 343, 345, 347, 349, 351, 353, 99 | 355, 357, 359, 361, 363, 365, 367, 369, 100 | 371, 373, 375, 377, 379, 381, 383, 385, 101 | 387, 389, 391, 393, 403, 407, 418, 429, 102 | }; 103 | 104 | static const uint_least16_t xkbextra[][2] = { 105 | {0xF001, 436}, // KF|1 → F1 106 | {0xF002, 439}, // KF|2 → F2 107 | {0xF003, 442}, // KF|3 → F3 108 | {0xF004, 445}, // KF|4 → F4 109 | {0xF005, 448}, // KF|5 → F5 110 | {0xF006, 451}, // KF|6 → F6 111 | {0xF007, 454}, // KF|7 → F7 112 | {0xF008, 457}, // KF|8 → F8 113 | {0xF009, 460}, // KF|9 → F9 114 | {0xF00A, 463}, // KF|10 → F10 115 | {0xF00B, 467}, // KF|11 → F11 116 | {0xF00C, 471}, // KF|12 → F12 117 | {0xF00D, 475}, // Khome → Home 118 | {0xF00E, 480}, // Kup → Up 119 | {0xF00F, 483}, // Kpgup → Prior 120 | {0xF010, 489}, // Kprint → Print 121 | {0xF011, 495}, // Kleft → Left 122 | {0xF012, 500}, // Kright → Right 123 | {0xF013, 506}, // Kpgdown → Next 124 | {0xF014, 511}, // Kins → Insert 125 | {0xF015, 518}, // Kalt → Alt_L 126 | {0xF016, 524}, // Kshift → Shift_L 127 | {0xF017, 532}, // Kctl → Control_L 128 | {0xF018, 542}, // Kend → End 129 | {0xF019, 546}, // Kscroll → Scroll_Lock 130 | {0xF022, 558}, // Ksbwd → XF86AudioPrev 131 | {0xF023, 572}, // Ksfwd → XF86AudioNext 132 | {0xF024, 586}, // Kpause → XF86AudioPlay 133 | {0xF025, 600}, // Kvoldn → XF86AudioLowerVolume 134 | {0xF026, 621}, // Kvolup → XF86AudioRaiseVolume 135 | {0xF027, 642}, // Kmute → XF86AudioMute 136 | {0xF028, 656}, // Kbrtdn → XF86MonBrightnessDown 137 | {0xF029, 678}, // Kbrtup → XF86MonBrightnessUp 138 | {0xF800, 698}, // Kdown → Down 139 | {0xF861, 703}, // Kbreak → Break 140 | {0xF864, 709}, // Kcaps → Caps_Lock 141 | {0xF865, 719}, // Knum → Num_Lock 142 | {0xF867, 728}, // Kaltgr → Alt_R 143 | {0xF868, 734}, // Kmod4 → Super_L 144 | }; 145 | 146 | static const char xkbtypes[] = 147 | "xkb_types \"plan9\" {\n" 148 | " type \"ONE_LEVEL\" {\n" 149 | " modifiers = none;\n" 150 | " level_name[1] = \"Any\";\n" 151 | " };\n" 152 | " type \"TWO_LEVEL\" {\n" 153 | " modifiers = Shift;\n" 154 | " map[Shift] = 2;\n" 155 | " level_name[1] = \"Base\";\n" 156 | " level_name[2] = \"Shift\";\n" 157 | " };\n" 158 | " type \"ALPHABETIC\" {\n" 159 | " modifiers = Shift+Lock;\n" 160 | " map[Shift] = 2;\n" 161 | " map[Lock] = 2;\n" 162 | " level_name[1] = \"Base\";\n" 163 | " level_name[2] = \"Caps\";\n" 164 | " };\n" 165 | /* 166 | " type \"KEYPAD\" {\n" 167 | " modifiers = Shift+NumLock;\n" 168 | " map[NumLock] = 2;\n" 169 | " level_name[1] = \"Base\";\n" 170 | " level_name[2] = \"Number\";\n" 171 | " };\n" 172 | */ 173 | "};\n"; 174 | 175 | static const char xkbcompat[] = 176 | "xkb_compat \"plan9\" {\n" 177 | " interpret Shift_L {\n" 178 | " action = SetMods(modifiers=Shift,clearLocks);\n" 179 | " };\n" 180 | " interpret Caps_Lock {\n" 181 | " action = LockMods(modifiers=Lock);\n" 182 | " };\n" 183 | "};\n"; 184 | 185 | static const char xkbmods[] = 186 | " modifier_map Mod1 {, };\n" 187 | " modifier_map Shift {};\n" 188 | " modifier_map Control {};\n" 189 | " modifier_map Lock {};\n" 190 | " modifier_map Mod4 {};\n"; 191 | 192 | struct key { 193 | uint_least16_t scan; 194 | uint_least16_t rune[10]; 195 | }; 196 | 197 | static int 198 | u16cmp(const void *a, const void *b) 199 | { 200 | uint_least16_t ra, rb; 201 | 202 | ra = *(uint_least16_t *)a; 203 | rb = *(uint_least16_t *)b; 204 | return ra < rb ? -1 : ra > rb; 205 | } 206 | 207 | static const char * 208 | runetoxkb(unsigned long r) 209 | { 210 | static char name[8]; 211 | uint_least16_t *k; 212 | unsigned i; 213 | 214 | assert(r != 0 && r <= 0x10FFFF); 215 | if (r < 128) { 216 | i = xkbascii[r]; 217 | } else { 218 | k = bsearch(&(uint_least16_t){r}, xkbextra, LEN(xkbextra), sizeof *xkbextra, u16cmp); 219 | i = k ? k[1] : 0; 220 | } 221 | if (i) 222 | return xkbnames + i; 223 | snprintf(name, sizeof name, "U%.4lX", r); 224 | return name; 225 | } 226 | 227 | int 228 | writekeymap(FILE *f, int (*nextline)(void *, char **, size_t *), void *aux) 229 | { 230 | char *pos; 231 | size_t len; 232 | int ret; 233 | unsigned long level, scan, rune; 234 | struct key *keys, *k; 235 | size_t keyslen, i; 236 | 237 | fputs("xkb_keymap {\n", f); 238 | fputs("xkb_keycodes \"plan9\" {\n", f); 239 | keys = NULL; 240 | keyslen = 0; 241 | for (;;) { 242 | ret = nextline(aux, &pos, &len); 243 | if (ret < 0) { 244 | free(keys); 245 | return -1; 246 | } 247 | if (ret == 0) 248 | break; 249 | level = strtoul(pos, &pos, 10); 250 | scan = strtoul(pos, &pos, 10); 251 | rune = strtoul(pos, &pos, 10); 252 | if (rune == 0 || rune > 0x10ffff) 253 | continue; 254 | switch (level) { 255 | case 2: /* esc1 */ 256 | case 5: /* esc1 ctrl */ 257 | case 6: /* esc1 shift */ 258 | case 0: 259 | for (k = keys; k < keys + keyslen; ++k) { 260 | if (k->rune[0] == rune) 261 | break; 262 | } 263 | if (k < keys + keyslen) { 264 | /* 265 | we can't distinguish between scan 266 | codes that map to the same rune at 267 | level 0, so use the first one 268 | */ 269 | //fprintf(stderr, "duplicate key %lu %.2lx %s\n", level, scan, runetoxkb(rune)); 270 | continue; 271 | } 272 | if ((keyslen & keyslen - 1) == 0 && keyslen - 1 > 127) { 273 | keys = realloc(keys, (keyslen ? keyslen * 2 : 128) * sizeof *keys); 274 | if (!keys) { 275 | perror(NULL); 276 | return -1; 277 | } 278 | } 279 | k = &keys[keyslen++]; 280 | k->scan = scan; 281 | k->rune[0] = rune; 282 | memset(k->rune + 1, 0, sizeof k->rune - sizeof k->rune[0]); 283 | fprintf(f, "\t = 0x%.4lX;\n", rune, rune + 8); 284 | break; 285 | default: 286 | k = bsearch(&(uint_least16_t){scan}, keys, keyslen, sizeof *keys, u16cmp); 287 | if (!k) 288 | continue; /* no unshifted rune */ 289 | k->rune[level] = rune; 290 | break; 291 | } 292 | } 293 | fputs("};\n", f); 294 | fwrite(xkbtypes, 1, sizeof xkbtypes - 1, f); 295 | fwrite(xkbcompat, 1, sizeof xkbcompat - 1, f); 296 | fputs("xkb_symbols \"plan9\" {\n", f); 297 | for (i = 0; i < keyslen; ++i) { 298 | k = &keys[i]; 299 | fprintf(f, "\tkey {[%s", k->rune[0], runetoxkb(k->rune[0])); 300 | if (k->rune[1] && k->rune[1] != k->rune[0]) 301 | fprintf(f, ", %s", runetoxkb(k->rune[1])); 302 | fputs("]};\n", f); 303 | } 304 | fwrite(xkbmods, 1, sizeof xkbmods - 1, f); 305 | fputs("};\n", f); 306 | fputs("};\n", f); 307 | fputc('\0', f); 308 | fflush(f); 309 | free(keys); 310 | if (ferror(f)) { 311 | perror("write"); 312 | return -1; 313 | } 314 | return 0; 315 | } 316 | 317 | uint32_t 318 | keymapmod(uint32_t key) 319 | { 320 | switch (key) { 321 | case 0xF015: return 1 << 3; /* Kalt → Mod1 */ 322 | case 0xF016: return 1 << 0; /* Kshift → Shift */ 323 | case 0xF017: return 1 << 2; /* Kctl → Control */ 324 | case 0xF864: return 1 << 1; /* Kcaps → Lock */ 325 | case 0xF867: return 1 << 3; /* Kaltgr → Mod1 */ 326 | case 0xF868: return 1 << 6; /* Kmod4 → Mod4 */ 327 | } 328 | return 0; 329 | } 330 | -------------------------------------------------------------------------------- /fs.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: ISC */ 2 | #define _POSIX_C_SOURCE 700 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "c9.h" 14 | #include "util.h" 15 | #include "fs.h" 16 | 17 | #define NOTAG 0xffff 18 | 19 | struct reply { 20 | C9r r; 21 | struct reply *next; 22 | uint32_t size; 23 | uint8_t data[]; 24 | }; 25 | 26 | struct callback { 27 | void (*fn)(C9r *, void *); 28 | void *aux; 29 | }; 30 | 31 | static C9error 32 | newtag(C9ctx *ctx, C9ttype type, C9tag *tagp) 33 | { 34 | C9aux *aux; 35 | int tag; 36 | struct callback *cb; 37 | 38 | aux = ctx->aux; 39 | tag = numget(&ctx->aux->tag); 40 | if (tag < 0) 41 | return C9Etag; 42 | if (tag >= aux->cblen) { 43 | cb = realloc(aux->cb, (tag + 1) * sizeof *aux->cb); 44 | if (!cb) 45 | return C9Etag; 46 | memset(cb + aux->cblen, 0, (tag + 1 - aux->cblen) * sizeof *aux->cb); 47 | aux->cb = cb; 48 | aux->cblen = tag + 1; 49 | } 50 | *tagp = tag; 51 | return 0; 52 | } 53 | 54 | static void 55 | freetag(C9ctx *ctx, C9tag tag) 56 | { 57 | numput(&ctx->aux->tag, tag); 58 | } 59 | 60 | static void 61 | write9p(C9ctx *ctx, int block) 62 | { 63 | C9aux *aux; 64 | ssize_t ret; 65 | struct pollfd pfd; 66 | 67 | aux = ctx->aux; 68 | while (aux->wpos < aux->wend) { 69 | ret = write(aux->wfd, aux->wpos, aux->wend - aux->wpos); 70 | if (ret < 0) { 71 | if (errno == EAGAIN) { 72 | if (!block) 73 | return; 74 | pfd.fd = aux->wfd; 75 | pfd.events = POLLOUT; 76 | poll(&pfd, 1, -1); 77 | continue; 78 | } 79 | snprintf(aux->err, sizeof aux->err, "write: %s", strerror(errno)); 80 | exit(1); 81 | } 82 | aux->wpos += ret; 83 | } 84 | aux->wpos = aux->wend = aux->wbuf; 85 | } 86 | 87 | static uint8_t * 88 | read9p(C9ctx *ctx, uint32_t size, int *err) 89 | { 90 | C9aux *aux; 91 | uint8_t *buf; 92 | ssize_t ret; 93 | 94 | aux = ctx->aux; 95 | assert(size <= sizeof aux->rbuf); 96 | if (aux->rend == aux->rpos || aux->rbuf + sizeof aux->rbuf - aux->rpos < size) { 97 | memmove(aux->rbuf, aux->rpos, aux->rend - aux->rpos); 98 | aux->rend = aux->rbuf + (aux->rend - aux->rpos); 99 | aux->rpos = aux->rbuf; 100 | } 101 | while (aux->rend - aux->rpos < size) { 102 | ret = read(aux->rfd, aux->rend, sizeof aux->rbuf - (aux->rend - aux->rbuf)); 103 | if (ret <= 0) { 104 | *err = ret == 0 || errno != EAGAIN; 105 | aux->ready = 0; 106 | return NULL; 107 | } 108 | aux->rend += ret; 109 | } 110 | buf = aux->rpos; 111 | aux->rpos += size; 112 | return buf; 113 | } 114 | 115 | static uint8_t * 116 | begin(C9ctx *ctx, uint32_t size) 117 | { 118 | C9aux *aux; 119 | uint8_t *buf; 120 | 121 | aux = ctx->aux; 122 | assert(size <= sizeof aux->wbuf); 123 | if (size > aux->wbuf + sizeof aux->wbuf - aux->wend) 124 | write9p(ctx, 1); 125 | buf = aux->wend; 126 | aux->wend += size; 127 | return buf; 128 | } 129 | 130 | static int 131 | end(C9ctx *ctx) 132 | { 133 | return 0; 134 | } 135 | 136 | static void 137 | r(C9ctx *ctx, C9r *r) 138 | { 139 | C9aux *aux; 140 | struct reply *reply; 141 | size_t size; 142 | 143 | aux = ctx->aux; 144 | size = sizeof *reply; 145 | switch (r->type) { 146 | case Rerror: 147 | size = strlen(r->error) + 1; 148 | reply = malloc(sizeof *reply + size); 149 | if (!reply) 150 | goto error; 151 | reply->r = *r; 152 | reply->r.error = (char *)reply->data; 153 | memcpy(reply->data, r->error, size); 154 | break; 155 | case Rread: 156 | size = r->read.size; 157 | reply = malloc(sizeof *reply + size); 158 | if (!reply) 159 | goto error; 160 | reply->r = *r; 161 | reply->r.read.data = reply->data; 162 | memcpy(reply->data, r->read.data, size); 163 | break; 164 | default: 165 | reply = malloc(sizeof *reply); 166 | if (!reply) 167 | goto error; 168 | reply->r = *r; 169 | } 170 | reply->next = aux->queue; 171 | aux->queue = reply; 172 | return; 173 | 174 | error: 175 | /* XXX: can we handle this better? */ 176 | perror(NULL); 177 | exit(1); 178 | } 179 | 180 | static void 181 | error(C9ctx *ctx, const char *fmt, ...) 182 | { 183 | C9aux *aux; 184 | va_list ap; 185 | int n; 186 | 187 | aux = ctx->aux; 188 | va_start(ap, fmt); 189 | n = vsnprintf(aux->err, sizeof aux->err - 1, fmt, ap); 190 | va_end(ap); 191 | if (n < 0 || (size_t)n >= sizeof aux->err - 1) 192 | strcpy(aux->err, "unknown error"); 193 | else 194 | aux->err[n] = 0; 195 | } 196 | 197 | static int 198 | fsversion(C9ctx *ctx, uint32_t msize) 199 | { 200 | C9aux *aux; 201 | C9tag tag; 202 | C9r *r; 203 | 204 | aux = ctx->aux; 205 | if (c9version(ctx, &tag, msize) != 0) 206 | goto error; 207 | r = fswait(ctx, tag, Rversion); 208 | if (!r) 209 | goto error; 210 | free(r); 211 | return 0; 212 | 213 | error: 214 | fprintf(stderr, "fsversion: %s\n", aux->err); 215 | return -1; 216 | } 217 | 218 | int 219 | fsinit(C9ctx *ctx, C9aux *aux) 220 | { 221 | ctx->newtag = newtag; 222 | ctx->freetag = freetag; 223 | ctx->read = read9p; 224 | ctx->begin = begin; 225 | ctx->end = end; 226 | ctx->r = r; 227 | ctx->error = error; 228 | ctx->aux = aux; 229 | aux->rpos = aux->rend = aux->rbuf; 230 | aux->wpos = aux->wend = aux->wbuf; 231 | aux->queue = NULL; 232 | aux->cb = NULL; 233 | aux->cblen = 0; 234 | fcntl(aux->rfd, F_SETFL, O_NONBLOCK); 235 | return fsversion(ctx, sizeof aux->rbuf); 236 | } 237 | 238 | void 239 | fsasync(C9ctx *ctx, C9tag tag, void (*fn)(C9r *, void *), void *data) 240 | { 241 | C9aux *aux; 242 | 243 | aux = ctx->aux; 244 | assert(tag < aux->cblen); 245 | aux->cb[tag].fn = fn; 246 | aux->cb[tag].aux = data; 247 | } 248 | 249 | C9r * 250 | fswait(C9ctx *ctx, C9tag tag, C9rtype type) 251 | { 252 | C9aux *aux; 253 | struct reply *r, **rp; 254 | struct pollfd pfd; 255 | 256 | write9p(ctx, 1); 257 | aux = ctx->aux; 258 | for (rp = &aux->queue; (r = *rp); rp = &r->next) { 259 | if (r->r.tag == tag) { 260 | *rp = r->next; 261 | goto found; 262 | } 263 | } 264 | pfd.fd = aux->rfd; 265 | pfd.events = POLLIN; 266 | aux->ready = 1; 267 | for (;;) { 268 | if (c9proc(ctx) != 0) { 269 | fprintf(stderr, "c9proc: %s\n", aux->err); 270 | exit(1); 271 | } 272 | if (!aux->ready) { 273 | if (poll(&pfd, 1, -1) != 1) { 274 | perror("poll"); 275 | exit(1); 276 | } 277 | aux->ready = 1; 278 | continue; 279 | } 280 | r = aux->queue; 281 | if (r->r.tag == tag) { 282 | aux->queue = r->next; 283 | goto found; 284 | } 285 | } 286 | found: 287 | freetag(ctx, r->r.tag); 288 | if (r->r.type == Rerror) { 289 | snprintf(aux->err, sizeof aux->err, "%s", r->r.error); 290 | free(r); 291 | r = NULL; 292 | } else if (r->r.type != type) { 293 | fprintf(stderr, "fswait: unexpected reply type: %d != %d\n", r->r.type, type); 294 | exit(1); 295 | } 296 | return &r->r; 297 | } 298 | 299 | void 300 | fswriteT(C9ctx *ctx) 301 | { 302 | write9p(ctx, 0); 303 | } 304 | 305 | void 306 | fsreadR(C9ctx *ctx) 307 | { 308 | C9aux *aux; 309 | 310 | aux = ctx->aux; 311 | aux->ready = 1; 312 | do { 313 | if (c9proc(ctx) != 0) { 314 | fprintf(stderr, "c9proc: %s\n", aux->err); 315 | exit(1); 316 | } 317 | } while (aux->ready); 318 | } 319 | 320 | void 321 | fsdispatch(C9ctx *ctx) 322 | { 323 | C9aux *aux; 324 | struct reply *r; 325 | struct callback cb; 326 | 327 | aux = ctx->aux; 328 | while (aux->queue) { 329 | r = aux->queue; 330 | aux->queue = r->next; 331 | cb.fn = NULL; 332 | if (r->r.tag < aux->cblen) { 333 | cb = aux->cb[r->r.tag]; 334 | aux->cb[r->r.tag].fn = NULL; 335 | } 336 | if (cb.fn) 337 | cb.fn(&r->r, cb.aux); 338 | else if (r->r.type == Rerror) 339 | fprintf(stderr, "fsdispatch: unhandled Rerror (tag %d): %s\n", (int)r->r.tag, r->r.error); 340 | //else 341 | // fprintf(stderr, "no callback for tag %d\n", r->tag); 342 | freetag(ctx, r->r.tag); 343 | free(r); 344 | } 345 | write9p(ctx, 0); 346 | } 347 | 348 | int 349 | fsflush(C9ctx *ctx, C9tag oldtag) 350 | { 351 | C9aux *aux; 352 | C9tag tag; 353 | struct reply *r, **rp; 354 | 355 | aux = ctx->aux; 356 | if (c9flush(ctx, &tag, oldtag) != 0) 357 | return -1; 358 | r = (struct reply *)fswait(ctx, tag, Rflush); 359 | if (!r) { 360 | fprintf(stderr, "Rerror reply to Tflush"); 361 | exit(1); 362 | } 363 | free(r); 364 | freetag(ctx, oldtag); 365 | if (oldtag < aux->cblen) 366 | aux->cb[oldtag].fn = NULL; 367 | for(rp = &aux->queue; (r = *rp); rp = &r->next) { 368 | if (r->r.tag == oldtag) { 369 | *rp = r->next; 370 | free(r); 371 | break; 372 | } 373 | } 374 | return 0; 375 | } 376 | 377 | int 378 | fsattach(C9ctx *ctx, const char *aname) 379 | { 380 | C9aux *aux; 381 | C9tag tag; 382 | C9r *r; 383 | int fid; 384 | 385 | aux = ctx->aux; 386 | fid = numget(&aux->fid); 387 | if (fid < 0) { 388 | perror("fsattach"); 389 | return -1; 390 | } 391 | if (c9attach(ctx, &tag, fid, C9nofid, NULL, aname) != 0) 392 | return -1; 393 | r = fswait(ctx, tag, Rattach); 394 | if (!r) { 395 | numput(&aux->fid, fid); 396 | return -1; 397 | } 398 | free(r); 399 | return fid; 400 | } 401 | 402 | int 403 | fswalk(C9ctx *ctx, C9tag *tagp, int fid, const char *path[]) 404 | { 405 | C9aux *aux; 406 | C9tag tag; 407 | C9r *r; 408 | int newfid; 409 | 410 | aux = ctx->aux; 411 | newfid = numget(&aux->fid); 412 | if (newfid < 0) { 413 | if (tagp) 414 | *tagp = NOTAG; 415 | snprintf(aux->err, sizeof aux->err, "%s", strerror(errno)); 416 | return -1; 417 | } 418 | if (c9walk(ctx, &tag, fid, newfid, path) != 0) 419 | goto error; 420 | if (tagp) { 421 | *tagp = tag; 422 | return newfid; 423 | } 424 | r = fswait(ctx, tag, Rwalk); 425 | if (!r) 426 | goto error; 427 | free(r); 428 | return newfid; 429 | 430 | error: 431 | numput(&aux->fid, newfid); 432 | if (tagp) 433 | *tagp = NOTAG; 434 | return -1; 435 | } 436 | 437 | int 438 | fsopen(C9ctx *ctx, C9tag *tagp, int fid, C9mode mode) 439 | { 440 | C9tag tag; 441 | C9r *r; 442 | 443 | if (tagp) 444 | *tagp = NOTAG; 445 | if (c9open(ctx, &tag, fid, mode) != 0) 446 | return -1; 447 | if (tagp) { 448 | *tagp = tag; 449 | return 0; 450 | } 451 | r = fswait(ctx, tag, Ropen); 452 | if (!r) 453 | return -1; 454 | free(r); 455 | return 0; 456 | } 457 | 458 | int 459 | fsread(C9ctx *ctx, C9tag *tagp, C9r **rp, int fid, uint64_t off, uint32_t len) 460 | { 461 | C9tag tag; 462 | C9r *r; 463 | 464 | assert(tagp || rp); 465 | if (tagp) 466 | *tagp = NOTAG; 467 | if (c9read(ctx, &tag, fid, off, len) != 0) 468 | return -1; 469 | if (tagp) { 470 | *tagp = tag; 471 | return 0; 472 | } 473 | r = fswait(ctx, tag, Rread); 474 | if (!r) 475 | return -1; 476 | *rp = r; 477 | return 0; 478 | } 479 | 480 | int 481 | fswrite(C9ctx *ctx, C9tag *tagp, int fid, uint64_t off, const void *buf, uint32_t len) 482 | { 483 | C9tag tag; 484 | C9r *r; 485 | 486 | if (tagp) 487 | *tagp = NOTAG; 488 | if (c9write(ctx, &tag, fid, off, buf, len) != 0) 489 | return -1; 490 | if (tagp) { 491 | *tagp = tag; 492 | return 0; 493 | } 494 | r = fswait(ctx, tag, Rwrite); 495 | if (!r) 496 | return -1; 497 | free(r); 498 | return 0; 499 | } 500 | 501 | int 502 | fsclunk(C9ctx *ctx, int fid) 503 | { 504 | C9tag tag; 505 | C9r *r; 506 | 507 | if (c9clunk(ctx, &tag, fid) != 0) 508 | return -1; 509 | r = fswait(ctx, tag, Rclunk); 510 | if (!r) 511 | return -1; 512 | free(r); 513 | return 0; 514 | } 515 | -------------------------------------------------------------------------------- /c9.h: -------------------------------------------------------------------------------- 1 | #ifdef __plan9__ 2 | typedef u64int uint64_t; 3 | typedef u32int uint32_t; 4 | typedef u16int uint16_t; 5 | typedef u8int uint8_t; 6 | #define __attribute__(...) 7 | #define NULL nil 8 | #endif 9 | 10 | typedef struct C9r C9r; 11 | typedef struct C9t C9t; 12 | typedef struct C9stat C9stat; 13 | typedef struct C9ctx C9ctx; 14 | typedef struct C9aux C9aux; 15 | typedef struct C9qid C9qid; 16 | typedef uint32_t C9fid; 17 | typedef uint16_t C9tag; 18 | 19 | /* Stat field is not changed if it's set to this value when calling c9wstat. */ 20 | #define C9nochange (~0) 21 | 22 | /* Special fid used with auth/attach to basically avoid authentication. */ 23 | #define C9nofid ((C9fid)~0) 24 | 25 | /* C9modes for opening a file. */ 26 | typedef enum 27 | { 28 | C9read = 0, 29 | C9write = 1, 30 | C9rdwr = 2, 31 | C9exec = 3, 32 | C9trunc = 0x10, 33 | C9rclose = 0x40, 34 | C9excl = 0x1000, 35 | }C9mode; 36 | 37 | typedef enum 38 | { 39 | /* User/owner. */ 40 | C9permur = 1<<8, /* Readable. */ 41 | C9permuw = 1<<7, /* Writable. */ 42 | C9permux = 1<<6, /* Executable. */ 43 | 44 | /* Group. */ 45 | C9permgr = 1<<5, 46 | C9permgw = 1<<4, 47 | C9permgx = 1<<3, 48 | 49 | /* Other. */ 50 | C9permor = 1<<2, 51 | C9permow = 1<<1, 52 | C9permox = 1<<0, 53 | }C9perm; 54 | 55 | /* Directory. */ 56 | #define C9permdir 0x80000000 57 | 58 | /* Bitmask of stat.mode. */ 59 | #define C9stdir 0x80000000 60 | #define C9stappend 0x40000000 61 | #define C9stexcl 0x20000000 62 | #define C9sttmp 0x04000000 63 | 64 | /* Limits. */ 65 | enum 66 | { 67 | C9maxflush = 8, /* Maximal number of outstanding flushes. [1-65535] */ 68 | C9maxstr = 0xffff, /* Maximal string length. [1-65535] */ 69 | C9minmsize = 4096, /* Minimal sane msize. [4096-...] */ 70 | C9maxpathel = 16, /* Maximal number of elements in a path. Do not change. */ 71 | }; 72 | 73 | /* Errors. */ 74 | typedef enum 75 | { 76 | C9Einit = -1, /* Initialization failed. */ 77 | C9Ever = -2, /* Protocol version doesn't match. */ 78 | C9Epkt = -3, /* Incoming packet error. */ 79 | C9Etag = -4, /* No free tags or bad tag. */ 80 | C9Ebuf = -5, /* No buffer space enough for a message. */ 81 | C9Epath = -6, /* Path is too long or just invalid. */ 82 | C9Eflush = -7, /* Limit of outstanding flushes reached. */ 83 | C9Esize = -8, /* Can't fit data in one message. */ 84 | C9Estr = -9 /* Bad string. */ 85 | }C9error; 86 | 87 | /* Request types. */ 88 | typedef enum 89 | { 90 | Tversion = 100, 91 | Tauth = 102, 92 | Tattach = 104, 93 | Tflush = 108, 94 | Twalk = 110, 95 | Topen = 112, 96 | Tcreate = 114, 97 | Tread = 116, 98 | Twrite = 118, 99 | Tclunk = 120, 100 | Tremove = 122, 101 | Tstat = 124, 102 | Twstat = 126 103 | }C9ttype; 104 | 105 | /* Response types. */ 106 | typedef enum 107 | { 108 | Rversion = 101, 109 | Rauth = 103, 110 | Rattach = 105, 111 | Rerror = 107, 112 | Rflush = 109, 113 | Rwalk = 111, 114 | Ropen = 113, 115 | Rcreate = 115, 116 | Rread = 117, 117 | Rwrite = 119, 118 | Rclunk = 121, 119 | Rremove = 123, 120 | Rstat = 125, 121 | Rwstat = 127 122 | }C9rtype; 123 | 124 | /* Unique file id type. */ 125 | typedef enum 126 | { 127 | C9qtdir = 1<<7, 128 | C9qtappend = 1<<6, 129 | C9qtexcl = 1<<5, 130 | C9qtauth = 1<<3, 131 | C9qttmp = 1<<2, 132 | C9qtfile = 0 133 | }C9qt; 134 | 135 | /* Unique file id. */ 136 | struct C9qid 137 | { 138 | uint64_t path; 139 | uint32_t version; 140 | C9qt type; 141 | }; 142 | 143 | /* 144 | * File stats. Version and muid are ignored on wstat. Dmdir bit 145 | * change in mode won't work on wstat. Set any integer field to 146 | * C9nochange to keep it unchanged on wstat. Set any string to NULL to 147 | * keep it unchanged. Strings can be empty (""), but never NULL after 148 | * stat call. 149 | */ 150 | struct C9stat 151 | { 152 | C9qid qid; /* Same as qid[0]. */ 153 | uint64_t size; /* Size of the file (in bytes). */ 154 | char *name; /* Name of the file. */ 155 | char *uid; /* Owner of the file. */ 156 | char *gid; /* Group of the file. */ 157 | char *muid; /* The user who modified the file last. */ 158 | uint32_t mode; /* Permissions. See C9st* and C9perm. */ 159 | uint32_t atime; /* Last access time. */ 160 | uint32_t mtime; /* Last modification time. */ 161 | }; 162 | 163 | /* Response data. */ 164 | struct C9r 165 | { 166 | union 167 | { 168 | char *error; 169 | 170 | struct 171 | { 172 | uint8_t *data; 173 | uint32_t size; 174 | }read; 175 | 176 | struct 177 | { 178 | uint32_t size; 179 | }write; 180 | 181 | /* File stats (only valid if type is Rstat). */ 182 | C9stat stat; 183 | 184 | /* 185 | * Qid(s). qid[0] is valid for auth/attach/create/stat/open. 186 | * More ids may be a result of a walk, see numqid. 187 | */ 188 | C9qid qid[C9maxpathel]; 189 | }; 190 | C9rtype type; /* Response type. */ 191 | 192 | /* 193 | * If not zero, is the maximum number of bytes that are guaranteed 194 | * to be read or written atomically, without breaking into multiple 195 | * messages. 196 | */ 197 | uint32_t iounit; 198 | 199 | int numqid; /* Number of valid unique ids in qid array. */ 200 | C9tag tag; /* Tag number. */ 201 | }; 202 | 203 | /* Request data. */ 204 | struct C9t 205 | { 206 | C9ttype type; 207 | C9tag tag; 208 | union 209 | { 210 | struct 211 | { 212 | char *uname; 213 | char *aname; 214 | C9fid afid; 215 | }attach; 216 | 217 | struct 218 | { 219 | char *uname; 220 | char *aname; 221 | C9fid afid; 222 | }auth; 223 | 224 | struct 225 | { 226 | char *name; 227 | uint32_t perm; 228 | C9mode mode; 229 | }create; 230 | 231 | struct 232 | { 233 | C9tag oldtag; 234 | }flush; 235 | 236 | struct 237 | { 238 | C9mode mode; 239 | }open; 240 | 241 | struct 242 | { 243 | uint64_t offset; 244 | uint32_t size; 245 | }read; 246 | 247 | struct 248 | { 249 | char *wname[C9maxpathel+1]; /* wname[16] is always NULL */ 250 | C9fid newfid; 251 | }walk; 252 | 253 | struct 254 | { 255 | uint64_t offset; 256 | uint8_t *data; 257 | uint32_t size; 258 | }write; 259 | 260 | C9stat wstat; 261 | }; 262 | C9fid fid; 263 | }; 264 | 265 | struct C9ctx 266 | { 267 | /* Return a tag to use for an upcoming T-message. */ 268 | C9error (*newtag)(C9ctx *c, C9ttype type, C9tag *tag); 269 | 270 | /* Mark a tag as no longer used. */ 271 | void (*freetag)(C9ctx *ctx, C9tag tag); 272 | 273 | /* 274 | * Should return a pointer to the data (exactly 'size' bytes) read. 275 | * Set 'err' to non-zero and return NULL in case of error. 276 | * 'err' set to zero (no error) should be used to return from c9process 277 | * early (timeout on read to do non-blocking operations, for example). 278 | */ 279 | uint8_t *(*read)(C9ctx *ctx, uint32_t size, int *err) __attribute__((nonnull(1, 3))); 280 | 281 | /* Should return a buffer to store 'size' bytes. Nil means no memory. */ 282 | uint8_t *(*begin)(C9ctx *ctx, uint32_t size) __attribute__((nonnull(1))); 283 | 284 | /* 285 | * Marks the end of a message. Callback may decide if any accumulated 286 | * messages should be sent to the server/client. 287 | */ 288 | int (*end)(C9ctx *ctx) __attribute__((nonnull(1))); 289 | 290 | /* Callback called every time a new R-message is received. */ 291 | void (*r)(C9ctx *ctx, C9r *r) __attribute__((nonnull(1, 2))); 292 | 293 | /* Callback called every time a new T-message is received. */ 294 | void (*t)(C9ctx *ctx, C9t *t) __attribute__((nonnull(1, 2))); 295 | 296 | /* Callback for error messages. */ 297 | void (*error)(C9ctx *ctx, const char *fmt, ...) __attribute__((nonnull(2), format(printf, 2, 3))); 298 | 299 | /* Auxiliary data, can be used by any of above callbacks. */ 300 | C9aux *aux; 301 | 302 | /* private stuff */ 303 | uint32_t msize; 304 | uint32_t sz; 305 | union { 306 | #ifndef C9_NO_CLIENT 307 | uint32_t flush[C9maxflush]; 308 | #endif 309 | #ifndef C9_NO_SERVER 310 | uint16_t svflags; 311 | #endif 312 | }; 313 | }; 314 | 315 | /* Parse one directory entry. */ 316 | extern C9error c9parsedir(C9ctx *c, C9stat *stat, uint8_t **data, uint32_t *size) __attribute__((nonnull(1, 2, 3))); 317 | 318 | #ifndef C9_NO_CLIENT 319 | 320 | extern C9error c9version(C9ctx *c, C9tag *tag, uint32_t msize) __attribute__((nonnull(1, 2))); 321 | extern C9error c9auth(C9ctx *c, C9tag *tag, C9fid afid, const char *uname, const char *aname) __attribute__((nonnull(1, 2))); 322 | extern C9error c9flush(C9ctx *c, C9tag *tag, C9tag oldtag) __attribute__((nonnull(1, 2))); 323 | extern C9error c9attach(C9ctx *c, C9tag *tag, C9fid fid, C9fid afid, const char *uname, const char *aname) __attribute__((nonnull(1, 2))); 324 | extern C9error c9walk(C9ctx *c, C9tag *tag, C9fid fid, C9fid newfid, const char *path[]) __attribute__((nonnull(1, 2, 5))); 325 | extern C9error c9open(C9ctx *c, C9tag *tag, C9fid fid, C9mode mode) __attribute__((nonnull(1, 2))); 326 | extern C9error c9create(C9ctx *c, C9tag *tag, C9fid fid, const char *name, uint32_t perm, C9mode mode) __attribute__((nonnull(1, 2, 4))); 327 | extern C9error c9read(C9ctx *c, C9tag *tag, C9fid fid, uint64_t offset, uint32_t count) __attribute__((nonnull(1, 2))); 328 | extern C9error c9write(C9ctx *c, C9tag *tag, C9fid fid, uint64_t offset, const void *in, uint32_t count) __attribute__((nonnull(1, 2, 5))); 329 | extern C9error c9wrstr(C9ctx *c, C9tag *tag, C9fid fid, const char *s) __attribute__((nonnull(1, 2, 4))); 330 | extern C9error c9clunk(C9ctx *c, C9tag *tag, C9fid fid) __attribute__((nonnull(1, 2))); 331 | extern C9error c9remove(C9ctx *c, C9tag *tag, C9fid fid) __attribute__((nonnull(1, 2))); 332 | extern C9error c9stat(C9ctx *c, C9tag *tag, C9fid fid) __attribute__((nonnull(1, 2))); 333 | extern C9error c9wstat(C9ctx *c, C9tag *tag, C9fid fid, const C9stat *s) __attribute__((nonnull(1, 2, 4))); 334 | 335 | /* 336 | * Wait until a response comes and process it. If the function returns 337 | * any error, context must be treated as 'broken' and no subsequent calls 338 | * should be made without reinitialization (c9version). 339 | */ 340 | extern C9error c9proc(C9ctx *c) __attribute__((nonnull(1))); 341 | 342 | #endif /* C9_NO_CLIENT */ 343 | 344 | #ifndef C9_NO_SERVER 345 | 346 | extern C9error s9version(C9ctx *c) __attribute__((nonnull(1))); 347 | extern C9error s9auth(C9ctx *c, C9tag tag, const C9qid *aqid) __attribute__((nonnull(1, 3))); 348 | extern C9error s9error(C9ctx *c, C9tag tag, const char *err) __attribute__((nonnull(1))); 349 | extern C9error s9attach(C9ctx *c, C9tag tag, const C9qid *qid) __attribute__((nonnull(1, 3))); 350 | extern C9error s9flush(C9ctx *c, C9tag tag) __attribute__((nonnull(1))); 351 | extern C9error s9walk(C9ctx *c, C9tag tag, C9qid *qids[]) __attribute__((nonnull(1, 3))); 352 | extern C9error s9open(C9ctx *c, C9tag tag, const C9qid *qid, uint32_t iounit) __attribute__((nonnull(1, 3))); 353 | extern C9error s9create(C9ctx *c, C9tag tag, const C9qid *qid, uint32_t iounit) __attribute__((nonnull(1, 3))); 354 | extern C9error s9read(C9ctx *c, C9tag tag, const void *data, uint32_t size) __attribute__((nonnull(1, 3))); 355 | extern C9error s9readdir(C9ctx *c, C9tag tag, C9stat *st[], int *num, uint64_t *offset, uint32_t size) __attribute__((nonnull(1, 3, 4))); 356 | extern C9error s9write(C9ctx *c, C9tag tag, uint32_t size) __attribute__((nonnull(1))); 357 | extern C9error s9clunk(C9ctx *c, C9tag tag) __attribute__((nonnull(1))); 358 | extern C9error s9remove(C9ctx *c, C9tag tag) __attribute__((nonnull(1))); 359 | extern C9error s9stat(C9ctx *c, C9tag tag, const C9stat *s) __attribute__((nonnull(1, 3))); 360 | extern C9error s9wstat(C9ctx *c, C9tag tag) __attribute__((nonnull(1))); 361 | 362 | extern C9error s9proc(C9ctx *c) __attribute__((nonnull(1))); 363 | 364 | #endif /* C9_NO_SERVER */ 365 | -------------------------------------------------------------------------------- /c9.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This is 9p client and server implementation which aims to be 3 | * correct, small and secure. It's the lowest level implementation. 4 | * It doesn't have much comments, mostly because it doesn't make 5 | * any sense to copy-paste protocol documentation, which 6 | * you can read at http://man.cat-v.org/plan_9/5/, see 'intro'. 7 | */ 8 | #ifdef __plan9__ 9 | #include 10 | #include 11 | #define PRIu32 "%ud" 12 | #define PRIu16 "%hud" 13 | #else 14 | #include 15 | #include 16 | #endif 17 | #include "c9.h" 18 | 19 | enum 20 | { 21 | Svver = 1<<0, 22 | }; 23 | 24 | #define safestrlen(s) (s == NULL ? 0 : (uint32_t)strlen(s)) 25 | #define maxread(c) (c->msize-4-4-1-2) 26 | #define maxwrite(c) maxread(c) 27 | 28 | static void 29 | w08(uint8_t **p, uint8_t x) 30 | { 31 | (*p)[0] = x; 32 | *p += 1; 33 | } 34 | 35 | static void 36 | w16(uint8_t **p, uint16_t x) 37 | { 38 | (*p)[0] = x; 39 | (*p)[1] = x>>8; 40 | *p += 2; 41 | } 42 | 43 | static void 44 | w32(uint8_t **p, uint32_t x) 45 | { 46 | (*p)[0] = x; 47 | (*p)[1] = x>>8; 48 | (*p)[2] = x>>16; 49 | (*p)[3] = x>>24; 50 | *p += 4; 51 | } 52 | 53 | static void 54 | w64(uint8_t **p, uint64_t x) 55 | { 56 | (*p)[0] = x; 57 | (*p)[1] = x>>8; 58 | (*p)[2] = x>>16; 59 | (*p)[3] = x>>24; 60 | (*p)[4] = x>>32; 61 | (*p)[5] = x>>40; 62 | (*p)[6] = x>>48; 63 | (*p)[7] = x>>56; 64 | *p += 8; 65 | } 66 | 67 | static void 68 | wcs(uint8_t **p, const char *s, int len) 69 | { 70 | w16(p, len); 71 | if(s != NULL){ 72 | memmove(*p, s, len); 73 | *p += len; 74 | } 75 | } 76 | 77 | static uint8_t 78 | r08(uint8_t **p) 79 | { 80 | *p += 1; 81 | return (*p)[-1]; 82 | } 83 | 84 | static uint16_t 85 | r16(uint8_t **p) 86 | { 87 | *p += 2; 88 | return (*p)[-2]<<0 | (*p)[-1]<<8; 89 | } 90 | 91 | static uint32_t 92 | r32(uint8_t **p) 93 | { 94 | *p += 4; 95 | return (*p)[-4]<<0 | (*p)[-3]<<8 | (*p)[-2]<<16 | (*p)[-1]<<24; 96 | } 97 | 98 | static uint64_t 99 | r64(uint8_t **p) 100 | { 101 | uint64_t v; 102 | 103 | v = r32(p); 104 | v |= (uint64_t)r32(p)<<32; 105 | return v; 106 | } 107 | 108 | #ifndef C9_NO_CLIENT 109 | 110 | static uint8_t * 111 | T(C9ctx *c, uint32_t size, C9ttype type, C9tag *tag, C9error *err) 112 | { 113 | uint8_t *p = NULL; 114 | 115 | if(size > c->msize-4-1-2){ 116 | c->error(c, "T: invalid size %"PRIu32, size); 117 | *err = C9Esize; 118 | }else if((*err = c->newtag(c, type, tag)) == 0){ 119 | size += 4+1+2; 120 | if((p = c->begin(c, size)) == NULL){ 121 | c->error(c, "T: no buffer for %"PRIu32" bytes", size); 122 | c->freetag(c, *tag); 123 | *err = C9Ebuf; 124 | }else{ 125 | *err = 0; 126 | w32(&p, size); 127 | w08(&p, type); 128 | w16(&p, *tag); 129 | } 130 | } 131 | return p; 132 | } 133 | 134 | C9error 135 | c9version(C9ctx *c, C9tag *tag, uint32_t msize) 136 | { 137 | uint8_t *b; 138 | C9error err; 139 | 140 | if(msize < C9minmsize){ 141 | c->error(c, "c9version: msize too small: %"PRIu32, msize); 142 | return C9Einit; 143 | } 144 | memset(c->flush, 0xff, sizeof(c->flush)); 145 | c->msize = msize; 146 | 147 | if((b = T(c, 4+2+6, Tversion, tag, &err)) != NULL){ 148 | w32(&b, msize); 149 | wcs(&b, "9P2000", 6); 150 | err = c->end(c); 151 | } 152 | return err; 153 | } 154 | 155 | C9error 156 | c9auth(C9ctx *c, C9tag *tag, C9fid afid, const char *uname, const char *aname) 157 | { 158 | uint8_t *b; 159 | uint32_t ulen = safestrlen(uname), alen = safestrlen(aname); 160 | C9error err; 161 | 162 | if(ulen > C9maxstr || alen > C9maxstr){ 163 | c->error(c, "c9auth: string too long: %"PRIu32" chars", ulen > alen ? ulen : alen); 164 | return C9Estr; 165 | } 166 | if((b = T(c, 4+2+ulen+2+alen, Tauth, tag, &err)) != NULL){ 167 | w32(&b, afid); 168 | wcs(&b, uname, ulen); 169 | wcs(&b, aname, alen); 170 | err = c->end(c); 171 | } 172 | return err; 173 | } 174 | 175 | C9error 176 | c9flush(C9ctx *c, C9tag *tag, C9tag oldtag) 177 | { 178 | uint8_t *b; 179 | C9error err; 180 | int i; 181 | 182 | for(i = 0; i < C9maxflush && c->flush[i] != (uint32_t)~0; i++); 183 | if(i == C9maxflush){ 184 | c->error(c, "c9flush: no free flush slots"); 185 | return C9Eflush; 186 | } 187 | if((b = T(c, 2, Tflush, tag, &err)) != NULL){ 188 | w16(&b, oldtag); 189 | err = c->end(c); 190 | //if(err == 0) 191 | // c->flush[i] = (uint32_t)oldtag<<16 | *tag; 192 | } 193 | return err; 194 | } 195 | 196 | C9error 197 | c9attach(C9ctx *c, C9tag *tag, C9fid fid, C9fid afid, const char *uname, const char *aname) 198 | { 199 | uint32_t ulen = safestrlen(uname), alen = safestrlen(aname); 200 | uint8_t *b; 201 | C9error err; 202 | 203 | if(ulen > C9maxstr || alen > C9maxstr){ 204 | c->error(c, "c9attach: string too long: %"PRIu32" chars", ulen > alen ? ulen : alen); 205 | return C9Estr; 206 | } 207 | if((b = T(c, 4+4+2+ulen+2+alen, Tattach, tag, &err)) != NULL){ 208 | w32(&b, fid); 209 | w32(&b, afid); 210 | wcs(&b, uname, ulen); 211 | wcs(&b, aname, alen); 212 | err = c->end(c); 213 | } 214 | return err; 215 | } 216 | 217 | C9error 218 | c9walk(C9ctx *c, C9tag *tag, C9fid fid, C9fid newfid, const char *path[]) 219 | { 220 | uint32_t i, j, sz; 221 | uint32_t len[C9maxpathel]; 222 | uint8_t *b; 223 | C9error err; 224 | 225 | for(sz = i = 0; i < (int)sizeof(len)/sizeof(len[0]) && path[i] != NULL; i++){ 226 | len[i] = safestrlen(path[i]); 227 | if(len[i] == 0 || len[i] > C9maxstr){ 228 | c->error(c, "c9walk: invalid path element: %"PRIu32" chars", len[i]); 229 | return C9Epath; 230 | } 231 | sz += 2 + len[i]; 232 | } 233 | if(path[i] != NULL){ 234 | c->error(c, "c9walk: invalid elements !(0 <= %"PRIu32" <= %d)", i, C9maxpathel); 235 | return C9Epath; 236 | } 237 | 238 | if((b = T(c, 4+4+2+sz, Twalk, tag, &err)) != NULL){ 239 | w32(&b, fid); 240 | w32(&b, newfid); 241 | w16(&b, i); 242 | for(j = 0; j < i; j++) 243 | wcs(&b, path[j], len[j]); 244 | err = c->end(c); 245 | } 246 | return err; 247 | } 248 | 249 | C9error 250 | c9open(C9ctx *c, C9tag *tag, C9fid fid, C9mode mode) 251 | { 252 | uint8_t *b; 253 | C9error err; 254 | 255 | if((b = T(c, 4+1, Topen, tag, &err)) != NULL){ 256 | w32(&b, fid); 257 | w08(&b, mode); 258 | err = c->end(c); 259 | } 260 | return err; 261 | } 262 | 263 | C9error 264 | c9create(C9ctx *c, C9tag *tag, C9fid fid, const char *name, uint32_t perm, C9mode mode) 265 | { 266 | uint32_t nlen = strlen(name); 267 | uint8_t *b; 268 | C9error err; 269 | 270 | if(nlen == 0 || nlen > C9maxstr){ 271 | c->error(c, "c9create: invalid name: %"PRIu32" chars", nlen); 272 | return C9Epath; 273 | } 274 | if((b = T(c, 4+2+nlen+4+1, Tcreate, tag, &err)) != NULL){ 275 | w32(&b, fid); 276 | wcs(&b, name, nlen); 277 | w32(&b, perm); 278 | w08(&b, mode); 279 | err = c->end(c); 280 | } 281 | return err; 282 | } 283 | 284 | C9error 285 | c9read(C9ctx *c, C9tag *tag, C9fid fid, uint64_t offset, uint32_t count) 286 | { 287 | uint8_t *b; 288 | C9error err; 289 | 290 | if((b = T(c, 4+8+4, Tread, tag, &err)) != NULL){ 291 | w32(&b, fid); 292 | w64(&b, offset); 293 | w32(&b, count); 294 | err = c->end(c); 295 | } 296 | return err; 297 | } 298 | 299 | C9error 300 | c9write(C9ctx *c, C9tag *tag, C9fid fid, uint64_t offset, const void *in, uint32_t count) 301 | { 302 | uint8_t *b; 303 | C9error err; 304 | 305 | if((b = T(c, 4+8+4+count, Twrite, tag, &err)) != NULL){ 306 | w32(&b, fid); 307 | w64(&b, offset); 308 | w32(&b, count); 309 | memmove(b, in, count); 310 | err = c->end(c); 311 | } 312 | return err; 313 | } 314 | 315 | C9error 316 | c9wrstr(C9ctx *c, C9tag *tag, C9fid fid, const char *s) 317 | { 318 | return c9write(c, tag, fid, 0, s, strlen(s)); 319 | } 320 | 321 | C9error 322 | c9clunk(C9ctx *c, C9tag *tag, C9fid fid) 323 | { 324 | uint8_t *b; 325 | C9error err; 326 | 327 | if((b = T(c, 4, Tclunk, tag, &err)) != NULL){ 328 | w32(&b, fid); 329 | err = c->end(c); 330 | } 331 | return err; 332 | } 333 | 334 | C9error 335 | c9remove(C9ctx *c, C9tag *tag, C9fid fid) 336 | { 337 | uint8_t *b; 338 | C9error err; 339 | 340 | if((b = T(c, 4, Tremove, tag, &err)) != NULL){ 341 | w32(&b, fid); 342 | err = c->end(c); 343 | } 344 | return err; 345 | } 346 | 347 | C9error 348 | c9stat(C9ctx *c, C9tag *tag, C9fid fid) 349 | { 350 | uint8_t *b; 351 | C9error err; 352 | 353 | if((b = T(c, 4, Tstat, tag, &err)) != NULL){ 354 | w32(&b, fid); 355 | err = c->end(c); 356 | } 357 | return err; 358 | } 359 | 360 | C9error 361 | c9wstat(C9ctx *c, C9tag *tag, C9fid fid, const C9stat *s) 362 | { 363 | uint32_t nlen = safestrlen(s->name), ulen = safestrlen(s->uid), glen = safestrlen(s->gid); 364 | uint32_t unusedsz = 2+4+13, statsz = unusedsz+4+4+4+8+2+nlen+2+ulen+2+glen+2; 365 | uint8_t *b; 366 | C9error err; 367 | 368 | if(nlen == 0 || nlen > C9maxstr){ 369 | c->error(c, "c9wstat: invalid name: %"PRIu32" chars", nlen); 370 | return C9Epath; 371 | } 372 | if(ulen > C9maxstr || glen > C9maxstr){ 373 | c->error(c, "c9wstat: string too long: %"PRIu32" chars", ulen > glen ? ulen : glen); 374 | return C9Estr; 375 | } 376 | if((b = T(c, 4+2+2+statsz, Twstat, tag, &err)) != NULL){ 377 | w32(&b, fid); 378 | w16(&b, statsz+2); 379 | w16(&b, statsz); 380 | memset(b, 0xff, unusedsz); /* leave type(2), dev(4) and qid(13) unchanged */ 381 | b += unusedsz; 382 | w32(&b, s->mode); 383 | w32(&b, s->atime); 384 | w32(&b, s->mtime); 385 | w64(&b, s->size); 386 | wcs(&b, s->name, nlen); 387 | wcs(&b, s->uid, ulen); 388 | wcs(&b, s->gid, glen); 389 | wcs(&b, NULL, 0); /* muid unchanged */ 390 | err = c->end(c); 391 | } 392 | return err; 393 | } 394 | 395 | C9error 396 | c9proc(C9ctx *c) 397 | { 398 | uint32_t i, sz, cnt, msize; 399 | uint8_t *b; 400 | int err; 401 | C9r r; 402 | 403 | sz = c->sz; 404 | if(sz == 0){ 405 | err = -1; 406 | if((b = c->read(c, 4, &err)) == NULL){ 407 | if(err != 0) 408 | c->error(c, "c9proc: short read"); 409 | return err == 0 ? 0 : C9Epkt; 410 | } 411 | sz = r32(&b); 412 | if(sz < 7 || sz > c->msize){ 413 | c->error(c, "c9proc: invalid packet size !(7 <= %"PRIu32" <= %"PRIu32")", sz, c->msize); 414 | return C9Epkt; 415 | } 416 | c->sz = sz; 417 | } 418 | 419 | sz -= 4; 420 | err = -1; 421 | if((b = c->read(c, sz, &err)) == NULL){ 422 | if(err != 0) 423 | c->error(c, "c9proc: short read"); 424 | return err == 0 ? 0 : C9Epkt; 425 | } 426 | c->sz = 0; 427 | 428 | r.type = r08(&b); 429 | r.tag = r16(&b); 430 | sz -= 3; 431 | r.numqid = 0; 432 | 433 | switch(r.type){ 434 | case Rread: 435 | if(sz < 4 || (cnt = r32(&b)) > sz-4) 436 | goto error; 437 | r.read.data = b; 438 | r.read.size = cnt; 439 | c->r(c, &r); 440 | break; 441 | 442 | case Rwrite: 443 | if(sz < 4 || (cnt = r32(&b)) > c->msize) 444 | goto error; 445 | r.write.size = cnt; 446 | c->r(c, &r); 447 | break; 448 | 449 | case Rwalk: 450 | if(sz < 2 || (cnt = r16(&b))*13 > sz-2) 451 | goto error; 452 | if(cnt > C9maxpathel){ 453 | c->error(c, "c9proc: Rwalk !(%"PRIu32" <= %d)", cnt, C9maxpathel); 454 | return C9Epath; 455 | } 456 | for(i = 0; i < cnt; i++){ 457 | r.qid[i].type = r08(&b); 458 | r.qid[i].version = r32(&b); 459 | r.qid[i].path = r64(&b); 460 | } 461 | r.numqid = cnt; 462 | c->r(c, &r); 463 | break; 464 | 465 | case Rstat: 466 | b += 2; sz -= 2; 467 | if((err = c9parsedir(c, &r.stat, &b, &sz)) != 0) 468 | return err; 469 | r.numqid = 1; 470 | c->r(c, &r); 471 | break; 472 | 473 | case Rflush: 474 | for(i = 0; i < C9maxflush; i++){ 475 | if((c->flush[i] & 0xffff) == r.tag){ 476 | c->freetag(c, c->flush[i]>>16); 477 | c->flush[i] = 0xffffffff; 478 | break; 479 | } 480 | } 481 | /* fallthrough */ 482 | case Rclunk: 483 | case Rremove: 484 | case Rwstat: 485 | c->r(c, &r); 486 | break; 487 | 488 | case Ropen: 489 | case Rcreate: 490 | if(sz < 17) 491 | goto error; 492 | r.qid[0].type = r08(&b); 493 | r.qid[0].version = r32(&b); 494 | r.qid[0].path = r64(&b); 495 | r.iounit = r32(&b); 496 | r.numqid = 1; 497 | c->r(c, &r); 498 | break; 499 | 500 | case Rerror: 501 | if(sz < 2 || (cnt = r16(&b)) > sz-2) 502 | goto error; 503 | r.error = memmove(b-1, b, cnt); 504 | r.error[cnt] = 0; 505 | c->r(c, &r); 506 | break; 507 | 508 | case Rauth: 509 | case Rattach: 510 | if(sz < 13) 511 | goto error; 512 | r.qid[0].type = r08(&b); 513 | r.qid[0].version = r32(&b); 514 | r.qid[0].path = r64(&b); 515 | r.numqid = 1; 516 | c->r(c, &r); 517 | break; 518 | 519 | case Rversion: 520 | if(sz < 4+2 || (msize = r32(&b)) < C9minmsize || (cnt = r16(&b)) > sz-4-2) 521 | goto error; 522 | if(cnt != 6 || memcmp(b, "9P2000", 6) != 0){ 523 | c->error(c, "invalid version"); 524 | return C9Ever; 525 | } 526 | if(msize < c->msize) 527 | c->msize = msize; 528 | c->r(c, &r); 529 | break; 530 | 531 | default: 532 | goto error; 533 | } 534 | return 0; 535 | error: 536 | c->error(c, "c9proc: invalid packet type %d", (int)r.type); 537 | return C9Epkt; 538 | } 539 | 540 | #endif /* C9_NO_CLIENT */ 541 | 542 | C9error 543 | c9parsedir(C9ctx *c, C9stat *stat, uint8_t **t, uint32_t *size) 544 | { 545 | uint8_t *b; 546 | uint32_t cnt, sz; 547 | 548 | sz = 0; 549 | if(*size < 49 || (sz = r16(t)) < 47 || *size < 2+sz) 550 | goto error; 551 | *size -= 2+sz; 552 | *t += 6; /* skip type(2) and dev(4) */ 553 | stat->qid.type = r08(t); 554 | stat->qid.version = r32(t); 555 | stat->qid.path = r64(t); 556 | stat->mode = r32(t); 557 | stat->atime = r32(t); 558 | stat->mtime = r32(t); 559 | stat->size = r64(t); 560 | sz -= 39; 561 | if((cnt = r16(t)) > sz-2) 562 | goto error; 563 | stat->name = (char*)*t; b = *t = *t+cnt; sz -= 2+cnt; 564 | if(sz < 2 || (cnt = r16(t)) > sz-2) 565 | goto error; 566 | stat->uid = (char*)*t; *b = 0; b = *t = *t+cnt; sz -= 2+cnt; 567 | if(sz < 2 || (cnt = r16(t)) > sz-2) 568 | goto error; 569 | stat->gid = (char*)*t; *b = 0; b = *t = *t+cnt; sz -= 2+cnt; 570 | if(sz < 2 || (cnt = r16(t)) > sz-2) 571 | goto error; 572 | stat->muid = memmove(*t-1, *t, cnt); *b = stat->muid[cnt] = 0; *t = *t+cnt; sz -= 2+cnt; 573 | *t += sz; 574 | return 0; 575 | error: 576 | c->error(c, "c9parsedir: invalid size: size=%"PRIu32" sz=%"PRIu32, *size, sz); 577 | return C9Epkt; 578 | } 579 | 580 | #ifndef C9_NO_SERVER 581 | 582 | static uint8_t * 583 | R(C9ctx *c, uint32_t size, C9rtype type, C9tag tag, C9error *err) 584 | { 585 | uint8_t *p = NULL; 586 | 587 | if(size > c->msize-4-1-2){ 588 | c->error(c, "R: invalid size %"PRIu32, size); 589 | *err = C9Esize; 590 | }else{ 591 | size += 4+1+2; 592 | if((p = c->begin(c, size)) == NULL){ 593 | c->error(c, "R: no buffer for %"PRIu32" bytes", size); 594 | *err = C9Ebuf; 595 | }else{ 596 | *err = 0; 597 | w32(&p, size); 598 | w08(&p, type); 599 | w16(&p, tag); 600 | } 601 | } 602 | return p; 603 | } 604 | 605 | C9error 606 | s9version(C9ctx *c) 607 | { 608 | uint8_t *b; 609 | C9error err; 610 | 611 | if((b = R(c, 4+2+6, Rversion, 0xffff, &err)) != NULL){ 612 | w32(&b, c->msize); 613 | wcs(&b, "9P2000", 6); 614 | err = c->end(c); 615 | }; 616 | return err; 617 | } 618 | 619 | C9error 620 | s9auth(C9ctx *c, C9tag tag, const C9qid *aqid) 621 | { 622 | uint8_t *b; 623 | C9error err; 624 | 625 | if((b = R(c, 13, Rauth, tag, &err)) != NULL){ 626 | w08(&b, aqid->type); 627 | w32(&b, aqid->version); 628 | w64(&b, aqid->path); 629 | err = c->end(c); 630 | } 631 | return err; 632 | } 633 | 634 | C9error 635 | s9error(C9ctx *c, C9tag tag, const char *ename) 636 | { 637 | uint32_t len = safestrlen(ename); 638 | uint8_t *b; 639 | C9error err; 640 | 641 | if(len > C9maxstr){ 642 | c->error(c, "s9error: invalid ename: %"PRIu32" chars", len); 643 | return C9Estr; 644 | } 645 | if((b = R(c, 2+len, Rerror, tag, &err)) != NULL){ 646 | wcs(&b, ename, len); 647 | err = c->end(c); 648 | } 649 | return err; 650 | } 651 | 652 | C9error 653 | s9attach(C9ctx *c, C9tag tag, const C9qid *qid) 654 | { 655 | uint8_t *b; 656 | C9error err; 657 | 658 | if((b = R(c, 13, Rattach, tag, &err)) != NULL){ 659 | w08(&b, qid->type); 660 | w32(&b, qid->version); 661 | w64(&b, qid->path); 662 | err = c->end(c); 663 | } 664 | return err; 665 | } 666 | 667 | C9error 668 | s9flush(C9ctx *c, C9tag tag) 669 | { 670 | C9error err; 671 | 672 | if(R(c, 0, Rflush, tag, &err) != NULL) 673 | err = c->end(c); 674 | return err; 675 | } 676 | 677 | C9error 678 | s9walk(C9ctx *c, C9tag tag, C9qid *qids[]) 679 | { 680 | uint32_t i, n; 681 | uint8_t *b; 682 | C9error err; 683 | 684 | for(n = 0; n < C9maxpathel && qids[n] != NULL; n++); 685 | if(n > C9maxpathel){ 686 | c->error(c, "s9walk: invalid elements !(0 <= %"PRIu32" <= %d)", n, C9maxpathel); 687 | return C9Epath; 688 | } 689 | 690 | if((b = R(c, 2+n*13, Rwalk, tag, &err)) != NULL){ 691 | w16(&b, n); 692 | for(i = 0; i < n; i++){ 693 | w08(&b, qids[i]->type); 694 | w32(&b, qids[i]->version); 695 | w64(&b, qids[i]->path); 696 | } 697 | err = c->end(c); 698 | } 699 | return err; 700 | } 701 | 702 | C9error 703 | s9open(C9ctx *c, C9tag tag, const C9qid *qid, uint32_t iounit) 704 | { 705 | uint8_t *b; 706 | C9error err; 707 | 708 | if((b = R(c, 13+4, Ropen, tag, &err)) != NULL){ 709 | w08(&b, qid->type); 710 | w32(&b, qid->version); 711 | w64(&b, qid->path); 712 | w32(&b, iounit); 713 | err = c->end(c); 714 | } 715 | return err; 716 | } 717 | 718 | C9error 719 | s9create(C9ctx *c, C9tag tag, const C9qid *qid, uint32_t iounit) 720 | { 721 | uint8_t *b; 722 | C9error err; 723 | 724 | if((b = R(c, 13+4, Rcreate, tag, &err)) != NULL){ 725 | w08(&b, qid->type); 726 | w32(&b, qid->version); 727 | w64(&b, qid->path); 728 | w32(&b, iounit); 729 | err = c->end(c); 730 | } 731 | return err; 732 | } 733 | 734 | C9error 735 | s9read(C9ctx *c, C9tag tag, const void *data, uint32_t size) 736 | { 737 | uint8_t *b; 738 | C9error err; 739 | 740 | if((b = R(c, 4+size, Rread, tag, &err)) != NULL){ 741 | w32(&b, size); 742 | memmove(b, data, size); 743 | err = c->end(c); 744 | } 745 | return err; 746 | } 747 | 748 | C9error 749 | s9write(C9ctx *c, C9tag tag, uint32_t size) 750 | { 751 | uint8_t *b; 752 | C9error err; 753 | 754 | if((b = R(c, 4, Rwrite, tag, &err)) != NULL){ 755 | w32(&b, size); 756 | err = c->end(c); 757 | } 758 | return err; 759 | } 760 | 761 | C9error 762 | s9readdir(C9ctx *c, C9tag tag, C9stat *st[], int *num, uint64_t *offset, uint32_t size) 763 | { 764 | uint8_t *b; 765 | const C9stat *s; 766 | uint32_t nlen, ulen, glen, mulen, m, n; 767 | C9error err; 768 | int i; 769 | 770 | if(size > c->msize-4-1-2) 771 | size = c->msize-4-1-2; 772 | 773 | m = 0; 774 | for(i = 0; i < *num; i++){ 775 | s = st[i]; 776 | nlen = safestrlen(s->name); 777 | ulen = safestrlen(s->uid); 778 | glen = safestrlen(s->gid); 779 | mulen = safestrlen(s->muid); 780 | 781 | if(nlen == 0 || nlen > C9maxstr){ 782 | c->error(c, "s9readdir: invalid name: %"PRIu32" chars", nlen); 783 | return C9Epath; 784 | } 785 | if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){ 786 | ulen = ulen > glen ? ulen : glen; 787 | ulen = ulen > mulen ? ulen : mulen; 788 | c->error(c, "s9readdir: string too long: %"PRIu32" chars", ulen); 789 | return C9Estr; 790 | } 791 | 792 | n = 2 + 2+4+13+4+4+4+8+2+nlen+2+ulen+2+glen+2+mulen; 793 | if(4+m+n > size) 794 | break; 795 | m += n; 796 | } 797 | 798 | if((b = R(c, 4+m, Rread, tag, &err)) != NULL){ 799 | *num = i; 800 | w32(&b, m); 801 | for(i = 0; i < *num; i++){ 802 | s = st[i]; 803 | nlen = safestrlen(s->name); 804 | ulen = safestrlen(s->uid); 805 | glen = safestrlen(s->gid); 806 | mulen = safestrlen(s->muid); 807 | w16(&b, 2+4+13+4+4+4+8+2+nlen+2+ulen+2+glen+2+mulen); 808 | w16(&b, 0xffff); /* type */ 809 | w32(&b, 0xffffffff); /* dev */ 810 | w08(&b, s->qid.type); 811 | w32(&b, s->qid.version); 812 | w64(&b, s->qid.path); 813 | w32(&b, s->mode); 814 | w32(&b, s->atime); 815 | w32(&b, s->mtime); 816 | w64(&b, s->size); 817 | wcs(&b, s->name, nlen); 818 | wcs(&b, s->uid, ulen); 819 | wcs(&b, s->gid, glen); 820 | wcs(&b, s->muid, mulen); 821 | } 822 | err = c->end(c); 823 | if(err == 0) 824 | *offset += m; 825 | } 826 | return err; 827 | } 828 | 829 | C9error 830 | s9clunk(C9ctx *c, C9tag tag) 831 | { 832 | C9error err; 833 | 834 | if(R(c, 0, Rclunk, tag, &err) != NULL) 835 | err = c->end(c); 836 | return err; 837 | } 838 | 839 | C9error 840 | s9remove(C9ctx *c, C9tag tag) 841 | { 842 | C9error err; 843 | 844 | if(R(c, 0, Rremove, tag, &err) != NULL) 845 | err = c->end(c); 846 | return err; 847 | } 848 | 849 | C9error 850 | s9stat(C9ctx *c, C9tag tag, const C9stat *s) 851 | { 852 | uint32_t nlen = safestrlen(s->name), ulen = safestrlen(s->uid); 853 | uint32_t glen = safestrlen(s->gid), mulen = safestrlen(s->name); 854 | uint32_t statsz = 2+4+13+4+4+4+8+2+nlen+2+ulen+2+glen+2+mulen; 855 | uint8_t *b; 856 | C9error err; 857 | 858 | if(nlen == 0 || nlen > C9maxstr){ 859 | c->error(c, "s9stat: invalid name: %"PRIu32" chars", nlen); 860 | return C9Epath; 861 | } 862 | if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){ 863 | ulen = ulen > glen ? ulen : glen; 864 | ulen = ulen > mulen ? ulen : mulen; 865 | c->error(c, "s9stat: string too long: %"PRIu32" chars", ulen); 866 | return C9Estr; 867 | } 868 | 869 | if((b = R(c, 2+2+statsz, Rstat, tag, &err)) != NULL){ 870 | w16(&b, statsz+2); 871 | w16(&b, statsz); 872 | w16(&b, 0xffff); /* type */ 873 | w32(&b, 0xffffffff); /* dev */ 874 | w08(&b, s->qid.type); 875 | w32(&b, s->qid.version); 876 | w64(&b, s->qid.path); 877 | w32(&b, s->mode); 878 | w32(&b, s->atime); 879 | w32(&b, s->mtime); 880 | w64(&b, s->size); 881 | wcs(&b, s->name, nlen); 882 | wcs(&b, s->uid, ulen); 883 | wcs(&b, s->gid, glen); 884 | wcs(&b, s->muid, mulen); 885 | err = c->end(c); 886 | } 887 | return err; 888 | } 889 | 890 | C9error 891 | s9wstat(C9ctx *c, C9tag tag) 892 | { 893 | C9error err; 894 | 895 | if(R(c, 0, Rwstat, tag, &err) != NULL) 896 | err = c->end(c); 897 | return err; 898 | } 899 | 900 | C9error 901 | s9proc(C9ctx *c) 902 | { 903 | uint32_t i, sz, cnt, n, msize; 904 | int readerr; 905 | uint8_t *b; 906 | C9error err; 907 | C9t t; 908 | 909 | sz = c->sz; 910 | if(sz == 0){ 911 | readerr = -1; 912 | if((b = c->read(c, 4, &readerr)) == NULL){ 913 | if(readerr != 0) 914 | c->error(c, "s9proc: short read"); 915 | return readerr == 0 ? 0 : C9Epkt; 916 | } 917 | sz = r32(&b); 918 | if(sz < 7 || sz > c->msize){ 919 | c->error(c, "s9proc: invalid packet size !(7 <= %"PRIu32" <= %"PRIu32")", sz, c->msize); 920 | return C9Epkt; 921 | } 922 | c->sz = sz; 923 | } 924 | 925 | sz -= 4; 926 | readerr = -1; 927 | if((b = c->read(c, sz, &readerr)) == NULL){ 928 | if(readerr != 0) 929 | c->error(c, "s9proc: short read"); 930 | return readerr == 0 ? 0 : C9Epkt; 931 | } 932 | c->sz = 0; 933 | 934 | t.type = r08(&b); 935 | t.tag = r16(&b); 936 | sz -= 3; 937 | 938 | if((c->svflags & Svver) == 0 && t.type != Tversion){ 939 | c->error(c, "s9proc: expected Tversion, got %d", (int)t.type); 940 | return C9Epkt; 941 | } 942 | 943 | switch(t.type){ 944 | case Tread: 945 | if(sz < 4+8+4) 946 | goto error; 947 | t.fid = r32(&b); 948 | t.read.offset = r64(&b); 949 | t.read.size = r32(&b); 950 | if(t.read.size > maxread(c)) 951 | t.read.size = maxread(c); 952 | c->t(c, &t); 953 | break; 954 | 955 | case Twrite: 956 | if(sz < 4+8+4) 957 | goto error; 958 | t.fid = r32(&b); 959 | t.write.offset = r64(&b); 960 | if((t.write.size = r32(&b)) < sz-4-8-4) 961 | goto error; 962 | if(t.write.size > maxwrite(c)) 963 | t.write.size = maxwrite(c); 964 | t.write.data = b; 965 | c->t(c, &t); 966 | break; 967 | 968 | case Tclunk: 969 | case Tstat: 970 | case Tremove: 971 | if(sz < 4) 972 | goto error; 973 | t.fid = r32(&b); 974 | c->t(c, &t); 975 | break; 976 | 977 | case Twalk: 978 | if(sz < 4+4+2) 979 | goto error; 980 | t.fid = r32(&b); 981 | t.walk.newfid = r32(&b); 982 | if((n = r16(&b)) > 16){ 983 | c->error(c, "s9proc: Twalk !(%"PRIu32" <= 16)", n); 984 | return C9Epath; 985 | } 986 | sz -= 4+4+2; 987 | if(n > 0){ 988 | for(i = 0; i < n; i++){ 989 | if(sz < 2 || (cnt = r16(&b)) > sz-2) 990 | goto error; 991 | if(cnt < 1){ 992 | c->error(c, "s9proc: Twalk invalid element [%"PRIu32"]", i); 993 | return C9Epath; 994 | } 995 | b[-2] = 0; 996 | t.walk.wname[i] = (char*)b; 997 | b += cnt; 998 | sz -= 2 + cnt; 999 | } 1000 | memmove(t.walk.wname[i-1]-1, t.walk.wname[i-1], (char*)b - t.walk.wname[i-1]); 1001 | t.walk.wname[i-1]--; 1002 | b[-1] = 0; 1003 | }else 1004 | i = 0; 1005 | t.walk.wname[i] = NULL; 1006 | c->t(c, &t); 1007 | break; 1008 | 1009 | case Topen: 1010 | if(sz < 4+1) 1011 | goto error; 1012 | t.fid = r32(&b); 1013 | t.open.mode = r08(&b); 1014 | c->t(c, &t); 1015 | break; 1016 | 1017 | case Twstat: 1018 | if(sz < 4+2) 1019 | goto error; 1020 | t.fid = r32(&b); 1021 | if((cnt = r16(&b)) > sz-4) 1022 | goto error; 1023 | if((err = c9parsedir(c, &t.wstat, &b, &cnt)) != 0){ 1024 | c->error(c, "s9proc"); 1025 | return err; 1026 | } 1027 | c->t(c, &t); 1028 | break; 1029 | 1030 | case Tcreate: 1031 | if(sz < 4+2+4+1) 1032 | goto error; 1033 | t.fid = r32(&b); 1034 | if((cnt = r16(&b)) < 1 || cnt > sz-4-2-4-1) 1035 | goto error; 1036 | t.create.name = (char*)b; 1037 | b += cnt; 1038 | t.create.perm = r32(&b); 1039 | t.create.mode = r08(&b); 1040 | t.create.name[cnt] = 0; 1041 | c->t(c, &t); 1042 | break; 1043 | 1044 | case Tflush: 1045 | if(sz < 2) 1046 | goto error; 1047 | t.flush.oldtag = r16(&b); 1048 | c->t(c, &t); 1049 | break; 1050 | 1051 | case Tversion: 1052 | if(sz < 4+2 || (msize = r32(&b)) < C9minmsize || (cnt = r16(&b)) > sz-4-2) 1053 | goto error; 1054 | if(cnt < 6 || memcmp(b, "9P2000", 6) != 0){ 1055 | if((b = R(c, 4+2+7, Rversion, 0xffff, &err)) != NULL){ 1056 | w32(&b, 0); 1057 | wcs(&b, "unknown", 7); 1058 | err = c->end(c); 1059 | c->error(c, "s9proc: invalid version"); 1060 | } 1061 | return C9Ever; 1062 | } 1063 | if(msize < c->msize) 1064 | c->msize = msize; 1065 | c->svflags |= Svver; 1066 | c->t(c, &t); 1067 | break; 1068 | 1069 | case Tattach: 1070 | if(sz < 4+4+2+2) 1071 | goto error; 1072 | t.fid = r32(&b); 1073 | t.attach.afid = r32(&b); 1074 | cnt = r16(&b); 1075 | sz -= 4+4+2; 1076 | if(cnt+2 > sz) 1077 | goto error; 1078 | t.attach.uname = (char*)b; 1079 | b += cnt; 1080 | cnt = r16(&b); 1081 | b[-2] = 0; 1082 | sz -= cnt+2; 1083 | if(cnt > sz) 1084 | goto error; 1085 | memmove(b-1, b, cnt); 1086 | t.attach.aname = (char*)b-1; 1087 | t.attach.aname[cnt] = 0; 1088 | c->t(c, &t); 1089 | break; 1090 | 1091 | case Tauth: 1092 | if(sz < 4+2+2) 1093 | goto error; 1094 | t.auth.afid = r32(&b); 1095 | cnt = r16(&b); 1096 | sz -= 4+2; 1097 | if(cnt+2 > sz) 1098 | goto error; 1099 | t.auth.uname = (char*)b; 1100 | b += cnt; 1101 | cnt = r16(&b); 1102 | b[-2] = 0; 1103 | sz -= cnt+2; 1104 | if(cnt > sz) 1105 | goto error; 1106 | memmove(b-1, b, cnt); 1107 | t.auth.aname = (char*)b-1; 1108 | t.auth.aname[cnt] = 0; 1109 | c->t(c, &t); 1110 | break; 1111 | 1112 | default: 1113 | goto error; 1114 | } 1115 | return 0; 1116 | error: 1117 | c->error(c, "s9proc: invalid packet (type=%d)", (int)t.type); 1118 | return C9Epkt; 1119 | } 1120 | 1121 | #endif /* C9_NO_SERVER */ 1122 | -------------------------------------------------------------------------------- /wl9.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: ISC */ 2 | #define _POSIX_C_SOURCE 200809L 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "arg.h" 19 | #include "c9.h" 20 | #include "keymap.h" 21 | #include "util.h" 22 | #include "fs.h" 23 | #include "xdg-shell-server-protocol.h" 24 | #include "server-decoration-server-protocol.h" 25 | 26 | #define BORDER 4 27 | 28 | struct damage { 29 | int x0, y0; 30 | int x1, y1; 31 | }; 32 | 33 | struct surface_state { 34 | struct wl_resource *buffer; 35 | struct wl_listener buffer_destroy; 36 | struct wl_list callbacks; 37 | struct damage damage; 38 | }; 39 | 40 | struct surface { 41 | struct wl_resource *resource; 42 | struct wl_resource *role; 43 | void (*commit)(struct surface *); 44 | struct surface_state pending, state; 45 | }; 46 | 47 | struct region { 48 | struct wl_resource *resource; 49 | }; 50 | 51 | struct window { 52 | struct surface *surface; 53 | struct wl_resource *xdgsurface; 54 | struct wl_resource *toplevel; 55 | struct wl_listener surface_destroy; 56 | uint32_t serial; 57 | int initial_commit; 58 | 59 | char name[32]; 60 | int x0, y0; 61 | int x1, y1; 62 | int button; 63 | int mousex, mousey; 64 | struct wl_array keys; 65 | int current; 66 | int hidden; 67 | 68 | /* wsys fids */ 69 | int wsys; 70 | int wctl; 71 | int winname; 72 | int label; 73 | int mouse; 74 | int kbd; 75 | 76 | /* pending i/o tags */ 77 | C9tag wctltag; 78 | C9tag mousetag; 79 | C9tag kbdtag; 80 | 81 | /* /dev/draw image id */ 82 | int image; 83 | }; 84 | 85 | struct drawcopy { 86 | struct window *w; 87 | struct wl_shm_buffer *b; 88 | struct damage d; 89 | int x, y; 90 | int dx, dy; 91 | }; 92 | 93 | struct snarfput { 94 | int snarf; 95 | unsigned long long offset; 96 | struct wl_resource *source; 97 | struct wl_event_source *event; 98 | }; 99 | 100 | struct snarfget { 101 | int fd; 102 | unsigned long long offset; 103 | struct wl_resource *offer; 104 | }; 105 | 106 | static struct wl_display *dpy; 107 | static struct wl_event_loop *evt; 108 | static struct wl_client *child; 109 | static const struct wl_data_offer_interface offer_impl; 110 | static struct { 111 | struct wl_list resources; 112 | } output; 113 | static struct { 114 | struct wl_list resources; 115 | } datadev; 116 | static struct { 117 | struct window *focus; 118 | struct wl_list active; 119 | struct wl_list inactive; 120 | } mouse; 121 | static struct { 122 | struct window *focus; 123 | struct wl_list active; 124 | struct wl_list inactive; 125 | uint32_t mods; 126 | int keymapfd; 127 | size_t keymapsize; 128 | } kbd; 129 | static struct { 130 | /* root fid */ 131 | int root; 132 | /* $wsys fid */ 133 | int wsys; 134 | /* /dev/snarf fid */ 135 | int snarf; 136 | 137 | struct wl_event_source *event; 138 | uint32_t eventmask; 139 | } term; 140 | static struct { 141 | int ctlfid; 142 | int datafid; 143 | int datafd; 144 | 145 | int x0, y0; 146 | int x1, y1; 147 | unsigned char *buf; 148 | size_t buflen; 149 | struct numtab imgid; 150 | } draw; 151 | 152 | static C9aux termaux; 153 | static C9ctx termctx; 154 | 155 | static void 156 | drawcopy(struct drawcopy *d) 157 | { 158 | int x, y, x0, y0, x1, y1; 159 | unsigned char *img, *buf, *pos; 160 | size_t n, stride; 161 | int done; 162 | C9tag tag; 163 | struct wl_resource *r, *tmp; 164 | 165 | x = d->x, y = d->y; 166 | stride = wl_shm_buffer_get_stride(d->b); 167 | img = wl_shm_buffer_get_data(d->b); 168 | img += x * 4 + y * stride; 169 | buf = draw.buf; 170 | *buf++ = 'y'; 171 | buf = putle32(buf, d->w->image); 172 | tag = -1; 173 | for (done = 0; !done;) { 174 | x0 = x; 175 | x1 = x + d->dx; 176 | if (x1 > d->d.x1) 177 | x1 = d->d.x1; 178 | 179 | y0 = y; 180 | y1 = y + d->dy; 181 | if (y1 > d->d.y1) 182 | y1 = d->d.y1; 183 | 184 | pos = putle32(buf, d->w->x0 + x0); 185 | pos = putle32(pos, d->w->y0 + y0); 186 | pos = putle32(pos, d->w->x0 + x1); 187 | pos = putle32(pos, d->w->y0 + y1); 188 | n = (x1 - x0) * 4; 189 | for (; y < y1; ++y) 190 | memcpy(pos, img, n), pos += n, img += stride; 191 | if (x1 < d->d.x1) 192 | x = x1, --y; 193 | else 194 | x = d->d.x0; 195 | if (y == d->d.y1) { 196 | done = 1; 197 | *pos++ = 'v'; 198 | } 199 | 200 | if (fswrite(&termctx, &tag, draw.datafid, 0, draw.buf, pos - draw.buf) != 0) { 201 | fprintf(stderr, "fswrite %s draw: %s\n", d->w->name, termaux.err); 202 | break; 203 | } 204 | } 205 | assert(tag != -1); 206 | free(fswait(&termctx, tag, Rwrite)); 207 | 208 | d->x = x; 209 | d->y = y; 210 | d->w->surface->pending.damage.x0 = -1; 211 | d->w->surface->pending.damage.y0 = -1; 212 | d->w->surface->pending.damage.x1 = -1; 213 | d->w->surface->pending.damage.y1 = -1; 214 | wl_resource_for_each_safe(r, tmp, &d->w->surface->state.callbacks) { 215 | wl_callback_send_done(r, 0); 216 | wl_resource_destroy(r); 217 | } 218 | } 219 | 220 | static int 221 | snarfput(int fd, uint32_t mask, void *data) 222 | { 223 | struct snarfput *put; 224 | char buf[8192]; 225 | size_t len; 226 | ssize_t ret; 227 | 228 | put = data; 229 | len = sizeof buf; 230 | if (termctx.msize < len) 231 | len = termctx.msize - IOHDRSZ; 232 | ret = read(fd, buf, len); 233 | if (ret <= 0) { 234 | if (ret < 0) 235 | perror("read wl_data_source"); 236 | close(fd); 237 | fsclunk(&termctx, put->snarf); 238 | wl_event_source_remove(put->event); 239 | wl_data_source_send_cancelled(put->source); 240 | free(put); 241 | return 0; 242 | } 243 | fswrite(&termctx, NULL, put->snarf, put->offset, buf, ret); 244 | put->offset += ret; 245 | return 0; 246 | } 247 | 248 | static void 249 | snarfget(C9r *reply, void *aux) 250 | { 251 | struct snarfget *get; 252 | C9tag tag; 253 | unsigned char *pos, *end; 254 | ssize_t ret; 255 | 256 | get = aux; 257 | if (reply->type == Rerror) { 258 | fprintf(stderr, "read snarf: %s\n", reply->error); 259 | goto free; 260 | } 261 | if (reply->read.size == 0) { 262 | goto free; 263 | } 264 | get->offset += reply->read.size; 265 | pos = reply->read.data; 266 | end = pos + reply->read.size; 267 | while (pos < end) { 268 | ret = write(get->fd, pos, end - pos); 269 | if (ret < 0) { 270 | perror("write wl_data_offer"); 271 | goto free; 272 | } 273 | pos += ret; 274 | } 275 | if (fsread(&termctx, &tag, NULL, term.snarf, get->offset, 1024) != 0) 276 | goto free; 277 | fsasync(&termctx, tag, snarfget, get); 278 | return; 279 | 280 | free: 281 | close(get->fd); 282 | free(get); 283 | } 284 | 285 | static void 286 | snarfoffer(struct window *w) 287 | { 288 | struct wl_resource *r, *offer; 289 | struct wl_client *c; 290 | uint32_t ver; 291 | 292 | c = wl_resource_get_client(w->xdgsurface); 293 | r = wl_resource_find_for_client(&datadev.resources, c); 294 | if (!r) 295 | return; 296 | ver = wl_resource_get_version(r); 297 | offer = wl_resource_create(c, &wl_data_offer_interface, ver, 0); 298 | if (offer) { 299 | wl_resource_set_implementation(offer, &offer_impl, NULL, NULL); 300 | wl_resource_set_user_data(r, offer); 301 | wl_data_device_send_data_offer(r, offer); 302 | wl_data_offer_send_offer(offer, "text/plain;charset=utf-8"); 303 | wl_data_device_send_selection(r, offer); 304 | } 305 | } 306 | 307 | static void 308 | addstate(struct wl_array *a, uint32_t s) 309 | { 310 | uint32_t *p; 311 | 312 | p = wl_array_add(a, sizeof s); 313 | if (p) 314 | *p = s; 315 | } 316 | 317 | static void 318 | mousefocus(struct window *w, int x, int y) 319 | { 320 | struct wl_client *c; 321 | struct wl_resource *r, *s, *tmp; 322 | uint32_t serial; 323 | 324 | serial = wl_display_next_serial(dpy); 325 | if (mouse.focus) { 326 | s = mouse.focus->surface->resource; 327 | wl_resource_for_each(r, &mouse.active) 328 | wl_pointer_send_leave(r, serial, s); 329 | wl_list_insert_list(&mouse.inactive, &mouse.active); 330 | wl_list_init(&mouse.active); 331 | } 332 | if (w) { 333 | c = wl_resource_get_client(w->xdgsurface); 334 | s = w->surface->resource; 335 | wl_resource_for_each_safe(r, tmp, &mouse.inactive) { 336 | if (wl_resource_get_client(r) != c) 337 | continue; 338 | wl_list_remove(wl_resource_get_link(r)); 339 | wl_list_insert(&mouse.active, wl_resource_get_link(r)); 340 | wl_pointer_send_enter(r, serial, s, x << 8, y << 8); 341 | } 342 | } 343 | mouse.focus = w; 344 | } 345 | 346 | static void 347 | kbdfocus(struct window *w) 348 | { 349 | static const struct wl_array nokeys; 350 | struct wl_client *c; 351 | struct wl_resource *r, *s, *tmp; 352 | uint32_t serial; 353 | 354 | if (kbd.focus == w) 355 | return; 356 | serial = wl_display_next_serial(dpy); 357 | if (kbd.focus) { 358 | s = kbd.focus->surface->resource; 359 | wl_resource_for_each(r, &kbd.active) 360 | wl_keyboard_send_leave(r, serial, s); 361 | wl_list_insert_list(&kbd.inactive, &kbd.active); 362 | wl_list_init(&kbd.active); 363 | } 364 | if (w) { 365 | c = wl_resource_get_client(w->xdgsurface); 366 | s = w->surface->resource; 367 | wl_resource_for_each_safe(r, tmp, &kbd.inactive) { 368 | if (wl_resource_get_client(r) != c) 369 | continue; 370 | wl_list_remove(wl_resource_get_link(r)); 371 | wl_list_insert(&kbd.active, wl_resource_get_link(r)); 372 | wl_keyboard_send_enter(r, serial, s, (struct wl_array *)&nokeys); 373 | } 374 | } 375 | kbd.focus = w; 376 | } 377 | 378 | static void 379 | wctlread(C9r *reply, void *data) 380 | { 381 | static struct wl_array states; 382 | struct window *w; 383 | char *pos, *current, *hidden, buf[sizeof w->name + 6]; 384 | int x0, y0, x1, y1, needconfig; 385 | size_t namelen; 386 | C9r *r; 387 | 388 | w = data; 389 | w->wctltag = -1; 390 | if (reply->type == Rerror) { 391 | xdg_toplevel_send_close(w->toplevel); 392 | return; 393 | } 394 | if (fsread(&termctx, &w->wctltag, NULL, w->wctl, 0, 72) != 0) 395 | return; 396 | fsasync(&termctx, w->wctltag, wctlread, w); 397 | if (reply->read.size != 72) { 398 | fprintf(stderr, "unexpected wctl read size: %"PRIu32" != 72\n", reply->read.size); 399 | return; 400 | } 401 | pos = (char *)reply->read.data; 402 | pos[71] = '\0'; 403 | x0 = strtol(pos, &pos, 10) + BORDER; 404 | y0 = strtol(pos, &pos, 10) + BORDER; 405 | x1 = strtol(pos, &pos, 10) - BORDER; 406 | y1 = strtol(pos, &pos, 10) - BORDER; 407 | current = strtok_r(pos, " ", &pos); 408 | hidden = strtok_r(NULL, " ", &pos); 409 | needconfig = 0; 410 | if (x0 != w->x0 || y0 != w->y0 || x1 != w->x1 || y1 != w->y1) { 411 | /* resized or moved */ 412 | if (x1 - x0 != w->x1 - w->x0 || y1 - y0 != w->y1 - w->y0) 413 | needconfig = 1; 414 | w->x0 = x0, w->y0 = y0; 415 | w->x1 = x1, w->y1 = y1; 416 | if (fsread(&termctx, NULL, &r, w->winname, 0, 31) != 0 || r->read.size > 31) 417 | return; 418 | namelen = r->read.size; 419 | memcpy(w->name, r->read.data, namelen); 420 | w->name[namelen] = '\0'; 421 | free(r); 422 | 423 | pos = buf; 424 | if (w->image >= 0) { 425 | *pos++ = 'f'; 426 | pos = putle32(pos, w->image); 427 | } else { 428 | w->image = numget(&draw.imgid); 429 | if (w->image < 0) 430 | return; 431 | } 432 | *pos++ = 'n'; 433 | pos = putle32(pos, w->image); 434 | *pos++ = namelen; 435 | memcpy(pos, w->name, namelen), pos += namelen; 436 | assert(pos - buf < sizeof buf); 437 | if (fswrite(&termctx, NULL, draw.datafid, 0, buf, pos - buf) != 0) { 438 | fprintf(stderr, "fswrite %s draw: %s\n", w->name, termaux.err); 439 | return; 440 | } 441 | if (!needconfig) { 442 | struct drawcopy d; 443 | size_t n; 444 | 445 | d.w = w; 446 | d.b = wl_shm_buffer_get(w->surface->pending.buffer); 447 | if (!d.b) 448 | return; 449 | d.d.x0 = 0; 450 | d.d.y0 = 0; 451 | d.d.x1 = x1 - x0; 452 | d.d.y1 = y1 - y0; 453 | d.x = d.d.x0; 454 | d.y = d.d.y0; 455 | d.dx = d.d.x1 - d.d.x0; 456 | n = draw.buflen - 22; 457 | if (n < 4 * d.dx) { 458 | d.dx = (4 * d.dx + n - 1) / n; 459 | d.dy = 1; 460 | } else { 461 | d.dy = n / (4 * d.dx); 462 | } 463 | drawcopy(&d); 464 | } 465 | } 466 | if (w->current != (strcmp(current, "current") == 0)) { 467 | needconfig = 1; 468 | w->current ^= 1; 469 | if (w->current) { 470 | kbdfocus(w); 471 | snarfoffer(w); 472 | } else { 473 | if (mouse.focus == w) 474 | mousefocus(NULL, 0, 0); 475 | if (kbd.focus == w) 476 | kbdfocus(NULL); 477 | } 478 | } 479 | if (w->hidden != (strcmp(hidden, "hidden") == 0)) { 480 | w->hidden ^= 1; 481 | } 482 | if (needconfig) { 483 | states.size = 0; 484 | addstate(&states, XDG_TOPLEVEL_STATE_MAXIMIZED); 485 | if (w->current) 486 | addstate(&states, XDG_TOPLEVEL_STATE_ACTIVATED); 487 | xdg_toplevel_send_configure(w->toplevel, x1 - x0, y1 - y0, &states); 488 | w->serial = wl_display_next_serial(dpy); 489 | xdg_surface_send_configure(w->xdgsurface, w->serial); 490 | } 491 | } 492 | 493 | static void 494 | mouseaxis(struct wl_resource *r, uint32_t time, int val) 495 | { 496 | uint32_t axis, ver; 497 | 498 | axis = WL_POINTER_AXIS_VERTICAL_SCROLL; 499 | ver = wl_resource_get_version(r); 500 | if (ver >= 8) 501 | wl_pointer_send_axis_value120(r, axis, 120 * val); 502 | else if (ver >= 5) 503 | wl_pointer_send_axis_discrete(r, axis, val); 504 | wl_pointer_send_axis(r, time, axis, 15 * val << 8); 505 | } 506 | 507 | static void 508 | mouseread(C9r *reply, void *data) 509 | { 510 | static const int button[] = {0x110, 0x112, 0x111}; 511 | struct window *w; 512 | char *pos; 513 | unsigned long x, y, b, t; 514 | struct wl_resource *r; 515 | uint32_t serial, state; 516 | unsigned long pressed, changed; 517 | int i; 518 | 519 | w = data; 520 | w->mousetag = -1; 521 | if (reply->type == Rerror) { 522 | fprintf(stderr, "fsread %s mouse: %s\n", w->name, reply->error); 523 | return; 524 | } 525 | if (fsread(&termctx, &w->mousetag, NULL, w->mouse, 0, 49) != 0) { 526 | fprintf(stderr, "fsread %s mouse: %s\n", w->name, termaux.err); 527 | return; 528 | } 529 | fsasync(&termctx, w->mousetag, mouseread, w); 530 | if (reply->read.size != 49) { 531 | fprintf(stderr, "unexpected mouse data: size %"PRIu32" %.*s", reply->read.size, (int)reply->read.size, reply->read.data); 532 | return; 533 | } 534 | pos = (char *)reply->read.data; 535 | pos[48] = 0; 536 | if (*pos != 'm') 537 | return; 538 | ++pos; 539 | x = strtoul(pos, &pos, 10) - w->x0; 540 | y = strtoul(pos, &pos, 10) - w->y0; 541 | b = strtoul(pos, &pos, 10); 542 | t = strtoul(pos, &pos, 10); 543 | if (mouse.focus != w) { 544 | mousefocus(w, x, y); 545 | } else if (x != w->mousex || y != w->mousey) { 546 | wl_resource_for_each(r, &mouse.active) 547 | wl_pointer_send_motion(r, t, x << 8, y << 8); 548 | } 549 | pressed = b & ~w->button; 550 | changed = b ^ w->button; 551 | wl_resource_for_each(r, &mouse.active) { 552 | for (i = 0; i < 3; i++) { 553 | if (~changed & 1ul << i) 554 | continue; 555 | serial = wl_display_next_serial(dpy); 556 | state = pressed & 1ul << i 557 | ? WL_POINTER_BUTTON_STATE_PRESSED 558 | : WL_POINTER_BUTTON_STATE_RELEASED; 559 | wl_pointer_send_button(r, serial, t, button[i], state); 560 | } 561 | if (pressed & 8) 562 | mouseaxis(r, t, -1); 563 | else if (pressed & 16) 564 | mouseaxis(r, t, +1); 565 | if (wl_resource_get_version(r) >= 5) 566 | wl_pointer_send_frame(r); 567 | } 568 | w->mousex = x; 569 | w->mousey = y; 570 | w->button = b; 571 | } 572 | 573 | static unsigned char * 574 | kbdevent(struct window *w, unsigned char *pos, unsigned char *end) 575 | { 576 | static struct wl_array keys; 577 | struct wl_resource *r; 578 | uint32_t *key, *oldkey, state, mods, serial, time, eventkey; 579 | struct timespec ts; 580 | size_t n, notfound; 581 | int needenter; 582 | 583 | end = memchr(pos, '\0', end - pos); 584 | if (!end) { 585 | fprintf(stderr, "kbd event is not null terminated\n"); 586 | return NULL; 587 | } 588 | switch (*pos) { 589 | case 'k': state = WL_KEYBOARD_KEY_STATE_PRESSED; break; 590 | case 'K': state = WL_KEYBOARD_KEY_STATE_RELEASED; break; 591 | default: return end + 1; 592 | } 593 | keys.size = 0; 594 | for (++pos; pos < end; pos += n) { 595 | key = wl_array_add(&keys, sizeof *key); 596 | if (!key) { 597 | perror(NULL); 598 | return NULL; 599 | } 600 | n = utf8dec(key, pos, end - pos); 601 | if (n == (size_t)-1) { 602 | fprintf(stderr, "kbd event contains invalid UTF-8\n"); 603 | return NULL; 604 | } 605 | } 606 | eventkey = 0; 607 | if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { 608 | if (keys.size < sizeof *key) { 609 | fprintf(stderr, "kbd press with no keys\n"); 610 | return NULL; 611 | } 612 | eventkey = *key; 613 | keys.size -= sizeof *key; 614 | } 615 | mods = 0; 616 | wl_array_for_each(key, &keys) 617 | mods |= keymapmod(*key); 618 | if (kbd.focus != w) { 619 | kbdfocus(w); 620 | needenter = 1; 621 | } else if (keys.size != w->keys.size - !state * sizeof *key) { 622 | needenter = 1; 623 | } else { 624 | notfound = 0; 625 | wl_array_for_each(oldkey, &w->keys) { 626 | wl_array_for_each(key, &keys) { 627 | if (*key == *oldkey) 628 | goto found; 629 | } 630 | ++notfound; 631 | if (state == WL_KEYBOARD_KEY_STATE_PRESSED) 632 | break; 633 | eventkey = *oldkey; 634 | found:; 635 | } 636 | needenter = notfound != (state == WL_KEYBOARD_KEY_STATE_RELEASED); 637 | } 638 | serial = wl_display_next_serial(dpy); 639 | if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) 640 | time = ts.tv_sec * 1000ul + ts.tv_sec / 1000000ul; 641 | else 642 | time = 0; 643 | if (needenter) { 644 | wl_resource_for_each(r, &kbd.active) { 645 | wl_keyboard_send_enter(r, serial, w->surface->resource, &keys); 646 | wl_keyboard_send_modifiers(r, serial, mods, 0, 0, 0); 647 | } 648 | kbd.focus = w; 649 | } 650 | if (!needenter || state == WL_KEYBOARD_KEY_STATE_PRESSED) { 651 | if (state == WL_KEYBOARD_KEY_STATE_PRESSED) 652 | mods |= keymapmod(eventkey); 653 | wl_resource_for_each(r, &kbd.active) { 654 | wl_keyboard_send_key(r, serial, time, eventkey, state); 655 | if (mods != kbd.mods) 656 | wl_keyboard_send_modifiers(r, serial, mods, 0, 0, 0); 657 | } 658 | } 659 | if (state == WL_KEYBOARD_KEY_STATE_PRESSED) 660 | keys.size += sizeof *key; 661 | kbd.mods = mods; 662 | if (wl_array_copy(&w->keys, &keys) != 0) { 663 | perror(NULL); 664 | return NULL; 665 | } 666 | return end + 1; 667 | } 668 | 669 | static void 670 | kbdread(C9r *reply, void *data) 671 | { 672 | struct window *w; 673 | unsigned char *pos, *end; 674 | 675 | w = data; 676 | w->kbdtag = -1; 677 | if (reply->type == Rerror) { 678 | fprintf(stderr, "fsread %s kbd: %s\n", w->name, reply->error); 679 | return; 680 | } 681 | if (fsread(&termctx, &w->kbdtag, NULL, w->kbd, 0, 256) != 0) { 682 | fprintf(stderr, "fsread %s kbd: %s\n", w->name, reply->error); 683 | return; 684 | } 685 | fsasync(&termctx, w->kbdtag, kbdread, w); 686 | pos = reply->read.data; 687 | end = pos + reply->read.size; 688 | while (pos && pos < end) 689 | pos = kbdevent(w, pos, end); 690 | } 691 | 692 | static int 693 | winnew(struct window *w) 694 | { 695 | struct { 696 | const char *name; 697 | int *fid; 698 | C9mode mode; 699 | void (*readcb)(C9r *, void *); 700 | int readsz; 701 | C9tag tag; 702 | int clunk, flush; 703 | } *f, files[] = { 704 | {"winname", &w->winname, C9read}, 705 | {"label", &w->label, C9write}, 706 | {"wctl", &w->wctl, C9rdwr, NULL, 72}, 707 | {"mouse", &w->mouse, C9read, mouseread, 49}, 708 | {"kbd", &w->kbd, C9read, kbdread, 256}, 709 | }; 710 | char aname[32]; 711 | C9r *r; 712 | 713 | if (wl_resource_get_client(w->xdgsurface) == child) { 714 | w->wsys = fswalk(&termctx, NULL, term.root, (const char *[]){"dev", 0}); 715 | if (w->wsys < 0) { 716 | fprintf(stderr, "fswalk /dev: %s\n", termaux.err); 717 | return -1; 718 | } 719 | child = NULL; 720 | } else { 721 | /* XXX: depends on exportfs patch */ 722 | snprintf(aname, sizeof aname, "%11d new", term.wsys); 723 | w->wsys = fsattach(&termctx, aname); 724 | if (w->wsys < 0) { 725 | fprintf(stderr, "fsattach wsys: %s\n", termaux.err); 726 | return -1; 727 | } 728 | } 729 | w->image = -1; 730 | 731 | for (f = files; f < files + LEN(files); f++) { 732 | f->clunk = 0; 733 | *f->fid = fswalk(&termctx, &f->tag, w->wsys, (const char *[]){f->name, 0}); 734 | if (*f->fid < 0) { 735 | fprintf(stderr, "fswalk %s: %s\n", f->name, termaux.err); 736 | goto error; 737 | } 738 | } 739 | for (f = files; f < files + LEN(files); f++) { 740 | r = fswait(&termctx, f->tag, Rwalk); 741 | if (!r) { 742 | fprintf(stderr, "walk %s: %s\n", f->name, termaux.err); 743 | goto error; 744 | } 745 | free(r); 746 | f->clunk = 1; 747 | if (fsopen(&termctx, &f->tag, *f->fid, f->mode) != 0) { 748 | fprintf(stderr, "open %s: %s\n", f->name, termaux.err); 749 | goto error; 750 | } 751 | } 752 | for (f = files; f < files + LEN(files); f++) { 753 | r = fswait(&termctx, f->tag, Ropen); 754 | if (!r) { 755 | fprintf(stderr, "open %s: %s\n", f->name, termaux.err); 756 | goto error; 757 | } 758 | f->flush = 0; 759 | if (r->type == Rerror) { 760 | free(r); 761 | goto error; 762 | } 763 | free(r); 764 | if (f->readsz == 0) 765 | continue; 766 | if (fsread(&termctx, &f->tag, NULL, *f->fid, 0, f->readsz) != 0) { 767 | fprintf(stderr, "read %s: %s\n", f->name, termaux.err); 768 | goto error; 769 | } 770 | if (f->readcb) 771 | fsasync(&termctx, f->tag, f->readcb, w); 772 | } 773 | r = fswait(&termctx, files[2].tag, Rread); 774 | if (!r) 775 | goto error; 776 | wctlread(r, w); 777 | w->mousetag = files[3].tag; 778 | w->kbdtag = files[4].tag; 779 | return 0; 780 | 781 | error: 782 | fsclunk(&termctx, w->wsys); 783 | w->wsys = -1; 784 | for (f = files; f < files + LEN(files); f++) { 785 | numput(&termaux.fid, *f->fid); 786 | if (f->flush) 787 | fsflush(&termctx, f->tag); 788 | if (f->clunk) 789 | fsclunk(&termctx, *f->fid); 790 | *f->fid = -1; 791 | } 792 | return -1; 793 | } 794 | 795 | /* generic resource destructor */ 796 | static void 797 | destroy(struct wl_client *c, struct wl_resource *r) 798 | { 799 | wl_resource_destroy(r); 800 | } 801 | 802 | /* wl_surface */ 803 | static void 804 | attach(struct wl_client *c, struct wl_resource *r, struct wl_resource *b, int32_t x, int32_t y) 805 | { 806 | struct surface *s; 807 | 808 | s = wl_resource_get_user_data(r); 809 | if (s->pending.buffer) 810 | wl_list_remove(&s->pending.buffer_destroy.link); 811 | s->pending.buffer = b; 812 | if (b) 813 | wl_resource_add_destroy_listener(b, &s->pending.buffer_destroy); 814 | } 815 | 816 | static void 817 | damage(struct wl_client *c, struct wl_resource *r, int32_t x0, int32_t y0, int32_t w, int32_t h) 818 | { 819 | struct surface *s; 820 | struct damage *d; 821 | int x1, y1; 822 | 823 | x1 = x0 + w; 824 | y1 = y0 + h; 825 | s = wl_resource_get_user_data(r); 826 | d = &s->pending.damage; 827 | /* TODO: better damage tracking */ 828 | if (d->x0 == -1 || x0 < d->x0) 829 | d->x0 = x0; 830 | if (d->y0 == -1 || y0 < d->y0) 831 | d->y0 = y0; 832 | if (d->x1 == -1 || x1 > d->x1) 833 | d->x1 = x1; 834 | if (d->y1 == -1 || y1 > d->y1) 835 | d->y1 = y1; 836 | } 837 | 838 | static void 839 | unlink_resource(struct wl_resource *r) 840 | { 841 | wl_list_remove(wl_resource_get_link(r)); 842 | } 843 | 844 | static void 845 | frame(struct wl_client *c, struct wl_resource *r, uint32_t id) 846 | { 847 | struct surface *s; 848 | struct wl_resource *cbr; 849 | 850 | s = wl_resource_get_user_data(r); 851 | cbr = wl_resource_create(c, &wl_callback_interface, 1, id); 852 | if (!cbr) { 853 | wl_resource_post_no_memory(r); 854 | return; 855 | } 856 | wl_resource_set_implementation(cbr, NULL, NULL, unlink_resource); 857 | wl_list_insert(s->pending.callbacks.prev, wl_resource_get_link(cbr)); 858 | } 859 | 860 | static void 861 | set_opaque_region(struct wl_client *c, struct wl_resource *r, struct wl_resource *reg) 862 | { 863 | } 864 | 865 | static void 866 | set_input_region(struct wl_client *c, struct wl_resource *r, struct wl_resource *reg) 867 | { 868 | } 869 | 870 | static void 871 | commit(struct wl_client *c, struct wl_resource *r) 872 | { 873 | struct surface *s; 874 | 875 | s = wl_resource_get_user_data(r); 876 | wl_list_insert_list(&s->state.callbacks, &s->pending.callbacks); 877 | wl_list_init(&s->pending.callbacks); 878 | if (s->commit) 879 | s->commit(s); 880 | if (s->state.buffer != s->pending.buffer) { 881 | if (s->state.buffer) { 882 | wl_buffer_send_release(s->state.buffer); 883 | wl_list_remove(&s->state.buffer_destroy.link); 884 | } 885 | if (s->pending.buffer) 886 | wl_resource_add_destroy_listener(s->pending.buffer, &s->state.buffer_destroy); 887 | s->state.buffer = s->pending.buffer; 888 | } 889 | } 890 | 891 | static void 892 | set_buffer_transform(struct wl_client *c, struct wl_resource *r, int32_t mode) 893 | { 894 | } 895 | 896 | static void 897 | set_buffer_scale(struct wl_client *c, struct wl_resource *r, int32_t scale) 898 | { 899 | if (scale != 1) 900 | wl_resource_post_error(r, WL_SURFACE_ERROR_INVALID_SCALE, "set_buffer_scale is not supported"); 901 | } 902 | 903 | static void 904 | offset(struct wl_client *c, struct wl_resource *r, int32_t x, int32_t y) 905 | { 906 | } 907 | 908 | static const struct wl_surface_interface surface_impl = { 909 | .destroy = destroy, 910 | .attach = attach, 911 | .damage = damage, 912 | .frame = frame, 913 | .set_opaque_region = set_opaque_region, 914 | .set_input_region = set_input_region, 915 | .commit = commit, 916 | .set_buffer_transform = set_buffer_transform, 917 | .set_buffer_scale = set_buffer_scale, 918 | .damage_buffer = damage, 919 | .offset = offset, 920 | }; 921 | 922 | static void 923 | surface_buffer_destroyed(struct wl_listener *listener, void *data) 924 | { 925 | struct surface_state *state; 926 | 927 | state = wl_container_of(listener, state, buffer_destroy); 928 | state->buffer = NULL; 929 | } 930 | 931 | static void 932 | destroy_surface(struct wl_resource *r) 933 | { 934 | struct surface *s; 935 | 936 | s = wl_resource_get_user_data(r); 937 | if (s->state.buffer) 938 | wl_list_remove(&s->state.buffer_destroy.link); 939 | if (s->pending.buffer) 940 | wl_list_remove(&s->pending.buffer_destroy.link); 941 | free(s); 942 | } 943 | 944 | /* wl_region */ 945 | static void 946 | region_add(struct wl_client *c, struct wl_resource *r, int32_t x, int32_t y, int32_t w, int32_t h) 947 | { 948 | } 949 | 950 | static const struct wl_region_interface region_impl = { 951 | .destroy = destroy, 952 | .add = region_add, 953 | }; 954 | 955 | /* wl_compositor */ 956 | static void 957 | create_surface(struct wl_client *c, struct wl_resource *r, uint32_t id) 958 | { 959 | struct surface *s; 960 | uint32_t ver; 961 | 962 | s = calloc(1, sizeof *s); 963 | if (!s) 964 | goto error; 965 | ver = wl_resource_get_version(r); 966 | s->resource = wl_resource_create(c, &wl_surface_interface, ver, id); 967 | if (!s->resource) 968 | goto error; 969 | s->pending.buffer_destroy.notify = surface_buffer_destroyed; 970 | s->pending.damage.x0 = -1; 971 | s->pending.damage.y0 = -1; 972 | s->pending.damage.x1 = -1; 973 | s->pending.damage.y1 = -1; 974 | s->state.buffer_destroy.notify = surface_buffer_destroyed; 975 | s->state.damage.x0 = -1; 976 | s->state.damage.y0 = -1; 977 | s->state.damage.x1 = -1; 978 | s->state.damage.y1 = -1; 979 | wl_list_init(&s->pending.callbacks); 980 | wl_list_init(&s->state.callbacks); 981 | wl_resource_set_implementation(s->resource, &surface_impl, s, destroy_surface); 982 | return; 983 | 984 | error: 985 | free(s); 986 | wl_client_post_no_memory(c); 987 | } 988 | 989 | static void 990 | create_region(struct wl_client *c, struct wl_resource *r, uint32_t id) 991 | { 992 | struct wl_resource *nr; 993 | uint32_t ver; 994 | 995 | ver = wl_resource_get_version(r); 996 | nr = wl_resource_create(c, &wl_region_interface, ver, id); 997 | if (!nr) { 998 | wl_client_post_no_memory(c); 999 | return; 1000 | } 1001 | wl_resource_set_implementation(nr, ®ion_impl, nr, NULL); 1002 | } 1003 | 1004 | static const struct wl_compositor_interface compositor_impl = { 1005 | .create_surface = create_surface, 1006 | .create_region = create_region, 1007 | }; 1008 | 1009 | static void 1010 | bind_compositor(struct wl_client *c, void *p, uint32_t ver, uint32_t id) 1011 | { 1012 | struct wl_resource *r; 1013 | 1014 | r = wl_resource_create(c, &wl_compositor_interface, ver, id); 1015 | if (!r) { 1016 | wl_client_post_no_memory(c); 1017 | return; 1018 | } 1019 | wl_resource_set_implementation(r, &compositor_impl, NULL, NULL); 1020 | } 1021 | 1022 | /* xdg_toplevel */ 1023 | static void 1024 | set_parent(struct wl_client *c, struct wl_resource *r, struct wl_resource *p) 1025 | { 1026 | } 1027 | 1028 | static void 1029 | set_title(struct wl_client *c, struct wl_resource *r, const char *title) 1030 | { 1031 | struct window *w; 1032 | 1033 | w = wl_resource_get_user_data(r); 1034 | if (w->label == -1) 1035 | return; 1036 | fswrite(&termctx, NULL, w->label, 0, title, strlen(title)); 1037 | } 1038 | 1039 | static void 1040 | set_app_id(struct wl_client *c, struct wl_resource *r, const char *appid) 1041 | { 1042 | } 1043 | 1044 | static void 1045 | show_window_menu(struct wl_client *c, struct wl_resource *r, struct wl_resource *seat, uint32_t serial, int32_t x, int32_t y) 1046 | { 1047 | } 1048 | 1049 | static void 1050 | move(struct wl_client *c, struct wl_resource *r, struct wl_resource *seat, uint32_t serial) 1051 | { 1052 | } 1053 | 1054 | static void 1055 | resize(struct wl_client *c, struct wl_resource *r, struct wl_resource *seat, uint32_t serial, uint32_t edges) 1056 | { 1057 | } 1058 | 1059 | static void 1060 | set_max_size(struct wl_client *c, struct wl_resource *r, int32_t w, int32_t h) 1061 | { 1062 | } 1063 | 1064 | static void 1065 | set_min_size(struct wl_client *c, struct wl_resource *r, int32_t w, int32_t h) 1066 | { 1067 | } 1068 | 1069 | static void 1070 | set_maximized(struct wl_client *c, struct wl_resource *r) 1071 | { 1072 | } 1073 | 1074 | static void 1075 | unset_maximized(struct wl_client *c, struct wl_resource *r) 1076 | { 1077 | } 1078 | 1079 | static void 1080 | set_fullscreen(struct wl_client *c, struct wl_resource *r, struct wl_resource *output) 1081 | { 1082 | } 1083 | 1084 | static void 1085 | unset_fullscreen(struct wl_client *c, struct wl_resource *r) 1086 | { 1087 | } 1088 | 1089 | static void 1090 | set_minimized(struct wl_client *c, struct wl_resource *r) 1091 | { 1092 | } 1093 | 1094 | static const struct xdg_toplevel_interface toplevel_impl = { 1095 | .destroy = destroy, 1096 | .set_parent = set_parent, 1097 | .set_title = set_title, 1098 | .set_app_id = set_app_id, 1099 | .show_window_menu = show_window_menu, 1100 | .move = move, 1101 | .resize = resize, 1102 | .set_max_size = set_max_size, 1103 | .set_min_size = set_min_size, 1104 | .set_maximized = set_maximized, 1105 | .unset_maximized = unset_maximized, 1106 | .set_fullscreen = set_fullscreen, 1107 | .unset_fullscreen = unset_fullscreen, 1108 | .set_minimized = set_minimized, 1109 | }; 1110 | 1111 | static void 1112 | toplevel_commit(struct surface *s) 1113 | { 1114 | struct window *w; 1115 | struct drawcopy d; 1116 | struct wl_resource *r; 1117 | struct wl_client *c; 1118 | size_t n; 1119 | 1120 | w = wl_resource_get_user_data(s->role); 1121 | if (w->initial_commit) { 1122 | c = wl_resource_get_client(w->xdgsurface); 1123 | r = wl_resource_find_for_client(&output.resources, c); 1124 | if (r) 1125 | wl_surface_send_enter(s->resource, r); 1126 | 1127 | winnew(w); 1128 | w->initial_commit = 0; 1129 | } 1130 | 1131 | d.w = w; 1132 | d.b = wl_shm_buffer_get(s->pending.buffer); 1133 | if (!d.b) 1134 | return; 1135 | d.d = s->pending.damage; 1136 | if (d.d.x0 == -1) 1137 | return; 1138 | if (d.d.x1 > w->x1 - w->x0) 1139 | d.d.x1 = w->x1 - w->x0; 1140 | if (d.d.y1 > w->y1 - w->y0) 1141 | d.d.y1 = w->y1 - w->y0; 1142 | d.x = d.d.x0; 1143 | d.y = d.d.y0; 1144 | d.dx = d.d.x1 - d.d.x0; 1145 | n = draw.buflen - 22; 1146 | if (n < 4 * d.dx) { 1147 | d.dx = (4 * d.dx + n - 1) / n; 1148 | d.dy = 1; 1149 | } else { 1150 | d.dy = n / (4 * d.dx); 1151 | } 1152 | drawcopy(&d); 1153 | } 1154 | 1155 | static void 1156 | toplevel_destroy(struct wl_resource *r) 1157 | { 1158 | struct window *w; 1159 | char buf[5]; 1160 | 1161 | w = wl_resource_get_user_data(r); 1162 | if (w->wsys != -1) { 1163 | fsclunk(&termctx, w->wsys); 1164 | fsclunk(&termctx, w->wctl); 1165 | if (w->wctltag != -1) 1166 | fsflush(&termctx, w->wctltag); 1167 | fsclunk(&termctx, w->winname); 1168 | fsclunk(&termctx, w->label); 1169 | fsclunk(&termctx, w->mouse); 1170 | if (w->mousetag != -1) 1171 | fsflush(&termctx, w->mousetag); 1172 | fsclunk(&termctx, w->kbd); 1173 | if (w->kbdtag != -1) 1174 | fsflush(&termctx, w->kbdtag); 1175 | } 1176 | if (w->image != -1) { 1177 | buf[0] = 'f'; 1178 | putle32(buf + 1, w->image); 1179 | if (fswrite(&termctx, NULL, draw.datafid, 0, buf, sizeof buf) != 0) 1180 | fprintf(stderr, "fswrite %s draw: %s\n", w->name, strerror(errno)); 1181 | } 1182 | w->toplevel = NULL; 1183 | w->surface->role = NULL; 1184 | w->surface->commit = NULL; 1185 | } 1186 | 1187 | /* xdg_popup */ 1188 | static void 1189 | grab(struct wl_client *c, struct wl_resource *r, struct wl_resource *seat, uint32_t serial) 1190 | { 1191 | } 1192 | 1193 | static void 1194 | reposition(struct wl_client *c, struct wl_resource *r, struct wl_resource *pos, uint32_t token) 1195 | { 1196 | } 1197 | 1198 | static const struct xdg_popup_interface popup_impl = { 1199 | .destroy = destroy, 1200 | .grab = grab, 1201 | .reposition = reposition, 1202 | }; 1203 | 1204 | /* wl_data_offer */ 1205 | static void 1206 | offer_accept(struct wl_client *c, struct wl_resource *r, uint32_t serial, const char *mime) 1207 | { 1208 | struct wl_resource *ddr; 1209 | 1210 | if (!mime || strcmp(mime, "text/plain;charset=utf-8") != 0) 1211 | return; 1212 | return; 1213 | ddr = wl_resource_find_for_client(&datadev.resources, c); 1214 | if (ddr) 1215 | wl_data_device_send_selection(ddr, r); 1216 | } 1217 | 1218 | static void 1219 | offer_receive(struct wl_client *c, struct wl_resource *r, const char *mime, int32_t fd) 1220 | { 1221 | struct snarfget *get; 1222 | C9tag tag; 1223 | 1224 | get = malloc(sizeof *get); 1225 | if (!get) { 1226 | perror(NULL); 1227 | close(fd); 1228 | return; 1229 | } 1230 | get->fd = fd; 1231 | get->offset = 0; 1232 | get->offer = r; 1233 | if (fsread(&termctx, &tag, NULL, term.snarf, 0, 1024) != 0) { 1234 | close(fd); 1235 | free(get); 1236 | return; 1237 | } 1238 | fsasync(&termctx, tag, snarfget, get); 1239 | } 1240 | 1241 | static void 1242 | offer_finish(struct wl_client *c, struct wl_resource *r) 1243 | { 1244 | } 1245 | 1246 | static void 1247 | offer_set_actions(struct wl_client *c, struct wl_resource *r, uint32_t actions, uint32_t preferred) 1248 | { 1249 | } 1250 | 1251 | static const struct wl_data_offer_interface offer_impl = { 1252 | .accept = offer_accept, 1253 | .receive = offer_receive, 1254 | .destroy = destroy, 1255 | .finish = offer_finish, 1256 | .set_actions = offer_set_actions, 1257 | }; 1258 | 1259 | /* xdg_surface */ 1260 | static void 1261 | get_toplevel(struct wl_client *c, struct wl_resource *r, uint32_t id) 1262 | { 1263 | struct window *w; 1264 | uint32_t ver; 1265 | 1266 | w = wl_resource_get_user_data(r); 1267 | ver = wl_resource_get_version(r); 1268 | w->toplevel = wl_resource_create(c, &xdg_toplevel_interface, ver, id); 1269 | if (!w->toplevel) { 1270 | wl_client_post_no_memory(c); 1271 | return; 1272 | } 1273 | wl_resource_set_implementation(w->toplevel, &toplevel_impl, w, toplevel_destroy); 1274 | w->surface->role = w->toplevel; 1275 | w->surface->commit = toplevel_commit; 1276 | } 1277 | 1278 | static void 1279 | get_popup(struct wl_client *c, struct wl_resource *r, uint32_t id, struct wl_resource *parentr, struct wl_resource *posr) 1280 | { 1281 | struct wl_resource *pr; 1282 | uint32_t ver; 1283 | 1284 | ver = wl_resource_get_version(r); 1285 | pr = wl_resource_create(c, &xdg_popup_interface, ver, id); 1286 | if (!pr) { 1287 | wl_client_post_no_memory(c); 1288 | return; 1289 | } 1290 | wl_resource_set_implementation(pr, &popup_impl, NULL, NULL); 1291 | } 1292 | 1293 | static void 1294 | set_window_geometry(struct wl_client *c, struct wl_resource *r, int32_t x, int32_t y, int32_t w, int32_t h) 1295 | { 1296 | } 1297 | 1298 | static void 1299 | ack_configure(struct wl_client *c, struct wl_resource *r, uint32_t serial) 1300 | { 1301 | } 1302 | 1303 | static const struct xdg_surface_interface xdg_surface_impl = { 1304 | .destroy = destroy, 1305 | .get_toplevel = get_toplevel, 1306 | .get_popup = get_popup, 1307 | .set_window_geometry = set_window_geometry, 1308 | .ack_configure = ack_configure, 1309 | }; 1310 | 1311 | static void 1312 | xdgsurface_surface_destroyed(struct wl_listener *listener, void *data) 1313 | { 1314 | struct window *w; 1315 | 1316 | w = wl_container_of(listener, w, surface_destroy); 1317 | wl_resource_destroy(w->xdgsurface); 1318 | } 1319 | 1320 | static void 1321 | xdg_surface_destroy(struct wl_resource *r) 1322 | { 1323 | struct window *w; 1324 | 1325 | w = wl_resource_get_user_data(r); 1326 | wl_list_remove(&w->surface_destroy.link); 1327 | if (w->surface->role) 1328 | wl_resource_destroy(w->surface->role); 1329 | if (kbd.focus == w) 1330 | kbdfocus(NULL); 1331 | if (mouse.focus == w) 1332 | mousefocus(NULL, 0, 0); 1333 | free(w); 1334 | } 1335 | 1336 | /* xdg_positioner */ 1337 | static void 1338 | set_size(struct wl_client *c, struct wl_resource *r, int32_t w, int32_t h) 1339 | { 1340 | } 1341 | 1342 | static void 1343 | set_anchor_rect(struct wl_client *c, struct wl_resource *r, int32_t x, int32_t y, int32_t w, int32_t h) 1344 | { 1345 | } 1346 | 1347 | static void 1348 | set_anchor(struct wl_client *c, struct wl_resource *r, uint32_t anchor) 1349 | { 1350 | } 1351 | 1352 | static void 1353 | set_gravity(struct wl_client *c, struct wl_resource *r, uint32_t gravity) 1354 | { 1355 | } 1356 | 1357 | static void 1358 | set_constraint_adjustment(struct wl_client *c, struct wl_resource *r, uint32_t adj) 1359 | { 1360 | } 1361 | 1362 | static void 1363 | set_offset(struct wl_client *c, struct wl_resource *r, int32_t x, int32_t y) 1364 | { 1365 | } 1366 | 1367 | static void 1368 | set_reactive(struct wl_client *c, struct wl_resource *r) 1369 | { 1370 | } 1371 | 1372 | static void 1373 | set_parent_size(struct wl_client *c, struct wl_resource *r, int32_t w, int32_t h) 1374 | { 1375 | } 1376 | 1377 | static void 1378 | set_parent_configure(struct wl_client *c, struct wl_resource *r, uint32_t serial) 1379 | { 1380 | } 1381 | 1382 | static const struct xdg_positioner_interface positioner_impl = { 1383 | .destroy = destroy, 1384 | .set_size = set_size, 1385 | .set_anchor_rect = set_anchor_rect, 1386 | .set_anchor = set_anchor, 1387 | .set_gravity = set_gravity, 1388 | .set_constraint_adjustment = set_constraint_adjustment, 1389 | .set_offset = set_offset, 1390 | .set_reactive = set_reactive, 1391 | .set_parent_size = set_parent_size, 1392 | .set_parent_configure = set_parent_configure, 1393 | }; 1394 | 1395 | /* xdg_wm_base */ 1396 | static void 1397 | create_positioner(struct wl_client *c, struct wl_resource *r, uint32_t id) 1398 | { 1399 | struct wl_resource *pr; 1400 | uint32_t ver; 1401 | 1402 | ver = wl_resource_get_version(r); 1403 | pr = wl_resource_create(c, &xdg_positioner_interface, ver, id); 1404 | if (!pr) { 1405 | wl_client_post_no_memory(c); 1406 | return; 1407 | } 1408 | wl_resource_set_implementation(pr, &positioner_impl, NULL, NULL); 1409 | } 1410 | 1411 | static void 1412 | get_xdg_surface(struct wl_client *c, struct wl_resource *r, uint32_t id, struct wl_resource *sr) 1413 | { 1414 | struct surface *s; 1415 | struct window *w; 1416 | uint32_t ver; 1417 | 1418 | s = wl_resource_get_user_data(sr); 1419 | w = calloc(1, sizeof *w); 1420 | if (!w) 1421 | goto error; 1422 | ver = wl_resource_get_version(r); 1423 | w->xdgsurface = wl_resource_create(c, &xdg_surface_interface, ver, id); 1424 | if (!w->xdgsurface) 1425 | goto error; 1426 | wl_array_init(&w->keys); 1427 | w->surface = s; 1428 | w->initial_commit = 1; 1429 | w->surface_destroy.notify = xdgsurface_surface_destroyed; 1430 | wl_resource_add_destroy_listener(s->resource, &w->surface_destroy); 1431 | w->wsys = -1; 1432 | w->wctl = -1; 1433 | w->wctltag = -1; 1434 | w->winname = -1; 1435 | w->label = -1; 1436 | w->mouse = -1; 1437 | w->mousetag = -1; 1438 | w->kbd = -1; 1439 | w->kbdtag = -1; 1440 | wl_resource_set_implementation(w->xdgsurface, &xdg_surface_impl, w, xdg_surface_destroy); 1441 | return; 1442 | 1443 | error: 1444 | free(w); 1445 | wl_client_post_no_memory(c); 1446 | } 1447 | 1448 | static void 1449 | pong(struct wl_client *c, struct wl_resource *r, uint32_t serial) 1450 | { 1451 | } 1452 | 1453 | static const struct xdg_wm_base_interface wm_impl = { 1454 | .destroy = destroy, 1455 | .create_positioner = create_positioner, 1456 | .get_xdg_surface = get_xdg_surface, 1457 | .pong = pong, 1458 | }; 1459 | 1460 | static void 1461 | bind_wm(struct wl_client *c, void *p, uint32_t ver, uint32_t id) 1462 | { 1463 | struct wl_resource *r; 1464 | 1465 | r = wl_resource_create(c, &xdg_wm_base_interface, ver, id); 1466 | if (!r) { 1467 | wl_client_post_no_memory(c); 1468 | return; 1469 | } 1470 | wl_resource_set_implementation(r, &wm_impl, NULL, NULL); 1471 | } 1472 | 1473 | /* wl_pointer */ 1474 | static void 1475 | set_cursor(struct wl_client *c, struct wl_resource *r, uint32_t serial, struct wl_resource *sr, int32_t x, int32_t y) 1476 | { 1477 | /* TODO: implement */ 1478 | } 1479 | 1480 | static const struct wl_pointer_interface pointer_impl = { 1481 | .set_cursor = set_cursor, 1482 | .release = destroy, 1483 | }; 1484 | 1485 | /* wl_keyboard */ 1486 | static const struct wl_keyboard_interface keyboard_impl = { 1487 | .release = destroy, 1488 | }; 1489 | 1490 | /* wl_seat */ 1491 | static void 1492 | get_pointer(struct wl_client *c, struct wl_resource *r, uint32_t id) 1493 | { 1494 | struct wl_resource *nr; 1495 | struct window *w; 1496 | uint32_t ver; 1497 | int active; 1498 | 1499 | ver = wl_resource_get_version(r); 1500 | nr = wl_resource_create(c, &wl_pointer_interface, ver, id); 1501 | if (!nr) { 1502 | wl_resource_post_no_memory(r); 1503 | return; 1504 | } 1505 | wl_resource_set_implementation(nr, &pointer_impl, NULL, unlink_resource); 1506 | w = mouse.focus; 1507 | active = w && wl_resource_get_client(r) == wl_resource_get_client(w->xdgsurface); 1508 | wl_list_insert(active ? &mouse.active : &mouse.inactive, wl_resource_get_link(nr)); 1509 | } 1510 | 1511 | static void 1512 | get_keyboard(struct wl_client *c, struct wl_resource *r, uint32_t id) 1513 | { 1514 | struct wl_resource *nr; 1515 | struct window *w; 1516 | uint32_t ver; 1517 | int active; 1518 | 1519 | ver = wl_resource_get_version(r); 1520 | nr = wl_resource_create(c, &wl_keyboard_interface, ver, id); 1521 | if (!nr) { 1522 | wl_resource_post_no_memory(r); 1523 | return; 1524 | } 1525 | wl_resource_set_implementation(nr, &keyboard_impl, NULL, unlink_resource); 1526 | wl_keyboard_send_keymap(nr, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, kbd.keymapfd, kbd.keymapsize); 1527 | w = kbd.focus; 1528 | active = w && wl_resource_get_client(r) == wl_resource_get_client(w->xdgsurface); 1529 | wl_list_insert(active ? &kbd.active : &kbd.inactive, wl_resource_get_link(nr)); 1530 | } 1531 | 1532 | static void 1533 | get_touch(struct wl_client *c, struct wl_resource *r, uint32_t id) 1534 | { 1535 | wl_resource_post_error(r, WL_SEAT_ERROR_MISSING_CAPABILITY, "no touch"); 1536 | } 1537 | 1538 | static const struct wl_seat_interface seat_impl = { 1539 | .get_pointer = get_pointer, 1540 | .get_keyboard = get_keyboard, 1541 | .get_touch = get_touch, 1542 | .release = destroy, 1543 | }; 1544 | 1545 | static void 1546 | bind_seat(struct wl_client *c, void *p, uint32_t ver, uint32_t id) 1547 | { 1548 | struct wl_resource *r; 1549 | 1550 | r = wl_resource_create(c, &wl_seat_interface, ver, id); 1551 | if (!r) { 1552 | wl_client_post_no_memory(c); 1553 | return; 1554 | } 1555 | wl_resource_set_implementation(r, &seat_impl, NULL, NULL); 1556 | 1557 | wl_seat_send_capabilities(r, WL_SEAT_CAPABILITY_KEYBOARD | WL_SEAT_CAPABILITY_POINTER); 1558 | } 1559 | 1560 | /* wl_data_source */ 1561 | static void 1562 | offer(struct wl_client *c, struct wl_resource *r, const char *mime) 1563 | { 1564 | } 1565 | 1566 | static void 1567 | set_actions(struct wl_client *c, struct wl_resource *r, uint32_t actions) 1568 | { 1569 | } 1570 | 1571 | static const struct wl_data_source_interface data_source_impl = { 1572 | .offer = offer, 1573 | .destroy = destroy, 1574 | .set_actions = set_actions, 1575 | }; 1576 | 1577 | /* wl_data_device */ 1578 | static void 1579 | create_data_source(struct wl_client *c, struct wl_resource *r, uint32_t id) 1580 | { 1581 | struct wl_resource *nr; 1582 | uint32_t ver; 1583 | 1584 | ver = wl_resource_get_version(r); 1585 | nr = wl_resource_create(c, &wl_data_source_interface, ver, id); 1586 | if (!nr) { 1587 | wl_resource_post_no_memory(r); 1588 | return; 1589 | } 1590 | wl_resource_set_implementation(nr, &data_source_impl, NULL, NULL); 1591 | } 1592 | 1593 | static void 1594 | start_drag(struct wl_client *c, struct wl_resource *r, struct wl_resource *source, struct wl_resource *origin, struct wl_resource *icon, uint32_t serial) 1595 | { 1596 | } 1597 | 1598 | static void 1599 | set_selection(struct wl_client *c, struct wl_resource *r, struct wl_resource *source, uint32_t serial) 1600 | { 1601 | struct snarfput *put; 1602 | int fd[2]; 1603 | 1604 | put = malloc(sizeof *put); 1605 | if (!put) { 1606 | perror(NULL); 1607 | return; 1608 | } 1609 | if (pipe(fd) != 0) { 1610 | perror("pipe"); 1611 | free(put); 1612 | return; 1613 | } 1614 | wl_data_source_send_send(source, "text/plain;charset=utf-8", fd[1]); 1615 | close(fd[1]); 1616 | put->offset = 0; 1617 | put->source = source; 1618 | put->snarf = fswalk(&termctx, NULL, term.root, (const char *[]){"dev", "snarf", 0}); 1619 | if (put->snarf < 0) { 1620 | close(fd[0]); 1621 | free(put); 1622 | return; 1623 | } 1624 | if (fsopen(&termctx, NULL, put->snarf, C9write) != 0) { 1625 | fsclunk(&termctx, put->snarf); 1626 | close(fd[0]); 1627 | free(put); 1628 | return; 1629 | } 1630 | put->event = wl_event_loop_add_fd(evt, fd[0], WL_EVENT_READABLE, snarfput, put); 1631 | } 1632 | 1633 | static const struct wl_data_device_interface data_device_impl = { 1634 | .start_drag = start_drag, 1635 | .set_selection = set_selection, 1636 | .release = destroy, 1637 | }; 1638 | 1639 | /* wl_data_device_manager */ 1640 | static void 1641 | get_data_device(struct wl_client *c, struct wl_resource *r, uint32_t id, struct wl_resource *seat) 1642 | { 1643 | struct wl_resource *nr; 1644 | uint32_t ver; 1645 | 1646 | ver = wl_resource_get_version(r); 1647 | nr = wl_resource_create(c, &wl_data_device_interface, ver, id); 1648 | if (!nr) { 1649 | wl_resource_post_no_memory(r); 1650 | return; 1651 | } 1652 | wl_resource_set_implementation(nr, &data_device_impl, NULL, unlink_resource); 1653 | wl_list_insert(&datadev.resources, wl_resource_get_link(nr)); 1654 | } 1655 | 1656 | static const struct wl_data_device_manager_interface dataman_impl = { 1657 | .create_data_source = create_data_source, 1658 | .get_data_device = get_data_device, 1659 | }; 1660 | 1661 | static void 1662 | bind_dataman(struct wl_client *c, void *p, uint32_t ver, uint32_t id) 1663 | { 1664 | struct wl_resource *r; 1665 | 1666 | r = wl_resource_create(c, &wl_data_device_manager_interface, ver, id); 1667 | if (!r) { 1668 | wl_client_post_no_memory(c); 1669 | return; 1670 | } 1671 | wl_resource_set_implementation(r, &dataman_impl, NULL, NULL); 1672 | } 1673 | 1674 | /* wl_output */ 1675 | static void 1676 | bind_output(struct wl_client *c, void *p, uint32_t ver, uint32_t id) 1677 | { 1678 | struct wl_resource *r; 1679 | 1680 | r = wl_resource_create(c, &wl_output_interface, ver, id); 1681 | if (!r) { 1682 | wl_client_post_no_memory(c); 1683 | return; 1684 | } 1685 | wl_resource_set_implementation(r, NULL, NULL, unlink_resource); 1686 | wl_list_insert(&output.resources, wl_resource_get_link(r)); 1687 | wl_output_send_geometry(r, 0, 0, 0, 0, WL_OUTPUT_SUBPIXEL_UNKNOWN, "plan9", "rio", WL_OUTPUT_TRANSFORM_NORMAL); 1688 | wl_output_send_mode(r, WL_OUTPUT_MODE_CURRENT, draw.x1 - draw.x0, draw.y1 - draw.y0, 0); 1689 | wl_output_send_done(r); 1690 | } 1691 | 1692 | static void 1693 | request_mode(struct wl_client *c, struct wl_resource *r, uint32_t mode) 1694 | { 1695 | } 1696 | 1697 | /* org_kde_kwin_server_decoration */ 1698 | static const struct org_kde_kwin_server_decoration_interface deco_impl = { 1699 | .release = destroy, 1700 | .request_mode = request_mode, 1701 | }; 1702 | 1703 | static void 1704 | decoration_create(struct wl_client *c, struct wl_resource *r, uint32_t id, struct wl_resource *surface) 1705 | { 1706 | struct wl_resource *dr; 1707 | uint32_t ver; 1708 | 1709 | ver = wl_resource_get_version(r); 1710 | dr = wl_resource_create(c, &org_kde_kwin_server_decoration_interface, ver, id); 1711 | if (!dr) { 1712 | wl_client_post_no_memory(c); 1713 | return; 1714 | } 1715 | wl_resource_set_implementation(dr, &deco_impl, NULL, NULL); 1716 | org_kde_kwin_server_decoration_send_mode(dr, ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER); 1717 | } 1718 | 1719 | /* org_kde_kwin_server_decoration_manager */ 1720 | static const struct org_kde_kwin_server_decoration_manager_interface decoman_impl = { 1721 | .create = decoration_create, 1722 | }; 1723 | 1724 | static void 1725 | bind_decoman(struct wl_client *c, void *p, uint32_t ver, uint32_t id) 1726 | { 1727 | struct wl_resource *r; 1728 | 1729 | r = wl_resource_create(c, &org_kde_kwin_server_decoration_manager_interface, ver, id); 1730 | if (!r) { 1731 | wl_client_post_no_memory(c); 1732 | return; 1733 | } 1734 | wl_resource_set_implementation(r, &decoman_impl, NULL, NULL); 1735 | org_kde_kwin_server_decoration_manager_send_default_mode(r, ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER); 1736 | } 1737 | 1738 | static int 1739 | drawinit(C9ctx *ctx) 1740 | { 1741 | C9aux *aux; 1742 | C9r *r; 1743 | C9tag tag; 1744 | char conn[12]; 1745 | 1746 | aux = ctx->aux; 1747 | if (numget(&draw.imgid) != 0) { 1748 | perror(NULL); 1749 | return -1; 1750 | } 1751 | 1752 | draw.buflen = 32768; 1753 | if (draw.datafd < 0) { 1754 | draw.ctlfid = fswalk(ctx, NULL, draw.datafid, (const char *[]){"dev", "draw", "new", 0}); 1755 | if (draw.ctlfid < 0) { 1756 | fprintf(stderr, "fswalk /dev/draw/new: %s\n", aux->err); 1757 | return -1; 1758 | } 1759 | if (fsopen(ctx, NULL, draw.ctlfid, C9read) != 0) { 1760 | fprintf(stderr, "fsopen /dev/draw/new: %s\n", aux->err); 1761 | return -1; 1762 | } 1763 | if (fsread(ctx, NULL, &r, draw.ctlfid, 0, 144) != 0) { 1764 | fprintf(stderr, "fsread /dev/draw/new: %s\n", aux->err); 1765 | return -1; 1766 | } 1767 | if (r->read.size < 96) { 1768 | fprintf(stderr, "fsread /dev/draw/new: too short\n"); 1769 | return -1; 1770 | } 1771 | r->read.data[11] = '\0'; 1772 | strcpy(conn, (char *)r->read.data + strspn((char *)r->read.data, " ")); 1773 | r->read.data[96] = '\0'; 1774 | draw.x0 = atoi((char *)r->read.data + 48); 1775 | draw.y0 = atoi((char *)r->read.data + 60); 1776 | draw.x1 = atoi((char *)r->read.data + 72); 1777 | draw.y1 = atoi((char *)r->read.data + 84); 1778 | free(r); 1779 | draw.datafid = fswalk(ctx, NULL, term.root, (const char *[]){"dev", "draw", conn, "data", 0}); 1780 | if (draw.datafid < 0) { 1781 | fprintf(stderr, "fswalk /dev/draw/%s/data: %s\n", conn, aux->err); 1782 | return -1; 1783 | } 1784 | if (fsopen(ctx, &tag, draw.datafid, C9write) != 0) { 1785 | fprintf(stderr, "fsopen /dev/draw/%s/data: %s\n", conn, aux->err); 1786 | return -1; 1787 | } 1788 | r = fswait(ctx, tag, Ropen); 1789 | if (!r) { 1790 | fprintf(stderr, "fsopen /dev/draw/%s/data: %s\n", conn, aux->err); 1791 | return -1; 1792 | } 1793 | if (r->iounit) 1794 | draw.buflen = r->iounit; 1795 | free(r); 1796 | } 1797 | draw.buf = malloc(draw.buflen); 1798 | if (!draw.buf) { 1799 | perror(NULL); 1800 | return -1; 1801 | } 1802 | 1803 | return 0; 1804 | } 1805 | 1806 | static int 1807 | kbmapline(void *aux, char **str, size_t *len) 1808 | { 1809 | static uint64_t off; 1810 | C9r *r; 1811 | int fid; 1812 | 1813 | fid = *(int *)aux; 1814 | if (fsread(&termctx, NULL, &r, fid, off, 36) != 0) { 1815 | fprintf(stderr, "read /dev/kbmap: %s\n", termaux.err); 1816 | return -1; 1817 | } 1818 | if (r->read.size == 0) 1819 | return 0; 1820 | if (r->read.size != 36 || r->read.data[r->read.size - 1] != '\n') { 1821 | fprintf(stderr, "read /dev/kbmap: unexpected line size\n"); 1822 | return -1; 1823 | } 1824 | off += r->read.size; 1825 | r->read.data[r->read.size - 1] = '\0'; 1826 | *str = (char *)r->read.data; 1827 | *len = r->read.size - 1; 1828 | return 1; 1829 | } 1830 | 1831 | static int 1832 | keymapinit(C9ctx *ctx) 1833 | { 1834 | C9aux *aux; 1835 | FILE *f; 1836 | int fid; 1837 | struct stat st; 1838 | 1839 | aux = ctx->aux; 1840 | f = tmpfile(); 1841 | if (!f) { 1842 | perror("tmpfile"); 1843 | return -1; 1844 | } 1845 | fid = fswalk(ctx, NULL, term.root, (const char *[]){"dev", "kbmap", 0}); 1846 | if (fid < 0) { 1847 | fprintf(stderr, "fswalk /dev/kbmap: %s\n", aux->err); 1848 | return -1; 1849 | } 1850 | if (fsopen(ctx, NULL, fid, C9read) != 0) { 1851 | fprintf(stderr, "fsopen /dev/kbmap: %s\n", aux->err); 1852 | return -1; 1853 | } 1854 | if (writekeymap(f, kbmapline, &fid) != 0) 1855 | return -1; 1856 | if (fsclunk(ctx, fid) != 0) 1857 | return -1; 1858 | kbd.keymapfd = dup(fileno(f)); 1859 | if (kbd.keymapfd < 0) { 1860 | perror("dup"); 1861 | return -1; 1862 | } 1863 | fclose(f); 1864 | if (fstat(kbd.keymapfd, &st) != 0) { 1865 | perror("fstat"); 1866 | return -1; 1867 | } 1868 | kbd.keymapsize = st.st_size; 1869 | return 0; 1870 | } 1871 | 1872 | static void 1873 | usage(void) 1874 | { 1875 | fprintf(stderr, "usage: wl9 [-t termrfd[,termwfd]] [-w wsysrfd[,wsyswfd]] [-d datawfd]\n"); 1876 | exit(1); 1877 | } 1878 | 1879 | static void 1880 | fdpair(char *s, int *rfd, int *wfd) 1881 | { 1882 | long fd; 1883 | 1884 | errno = 0; 1885 | fd = strtol(s, &s, 10); 1886 | if (errno != 0 || fd < 0 || fd > INT_MAX) 1887 | usage(); 1888 | *rfd = fd; 1889 | if (wfd && *s == ',') { 1890 | errno = 0; 1891 | fd = strtol(s + 1, &s, 10); 1892 | if (errno != 0 || fd < 0 || fd > INT_MAX || *s != '\0') 1893 | usage(); 1894 | } else if (*s != '\0') { 1895 | usage(); 1896 | } 1897 | if (wfd) 1898 | *wfd = fd; 1899 | } 1900 | 1901 | char * 1902 | termenv(const char *var) 1903 | { 1904 | C9r *r; 1905 | int fid; 1906 | char *val; 1907 | 1908 | fid = fswalk(&termctx, NULL, term.root, (const char*[]){"env", var, 0}); 1909 | if (fid < 0) { 1910 | fprintf(stderr, "fswalk /env/%s: %s\n", var, termaux.err); 1911 | return NULL; 1912 | } 1913 | if (fsopen(&termctx, NULL, fid, C9read) != 0) { 1914 | fprintf(stderr, "fsopen /env/%s: %s\n", var, termaux.err); 1915 | fsclunk(&termctx, fid); 1916 | return NULL; 1917 | } 1918 | if (fsread(&termctx, NULL, &r, fid, 0, 128) != 0) { 1919 | fprintf(stderr, "fsread /env/%s: %s\n", var, termaux.err); 1920 | fsclunk(&termctx, fid); 1921 | return NULL; 1922 | } 1923 | val = malloc(r->read.size + 1); 1924 | if (!val) { 1925 | perror(NULL); 1926 | return NULL; 1927 | } 1928 | memcpy(val, r->read.data, r->read.size); 1929 | val[r->read.size] = '\0'; 1930 | return val; 1931 | } 1932 | 1933 | static char * 1934 | splitpath(char *path, const char *wname[], size_t wnamemax) 1935 | { 1936 | size_t nwname; 1937 | 1938 | if (path[0] != '/') 1939 | return "not an absolute path"; 1940 | nwname = 0; 1941 | for (; *path; ++path) { 1942 | if (nwname + 1 >= wnamemax) 1943 | return "too many components"; 1944 | if (*path == '/') { 1945 | wname[nwname++] = path + 1; 1946 | *path = '\0'; 1947 | } 1948 | } 1949 | wname[nwname] = NULL; 1950 | return NULL; 1951 | } 1952 | 1953 | static void 1954 | child_destroyed(struct wl_listener *l, void *data) 1955 | { 1956 | child = NULL; 1957 | } 1958 | 1959 | static void 1960 | launch(char *argv[]) 1961 | { 1962 | static struct wl_listener child_destroy; 1963 | extern char **environ; 1964 | int fd[2], err; 1965 | char fdstr[(sizeof fd[0] * CHAR_BIT + 2) / 3 + 1]; 1966 | pid_t pid; 1967 | 1968 | if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fd) != 0) { 1969 | perror("socketpair"); 1970 | exit(1); 1971 | } 1972 | if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) != 0) { 1973 | perror("fcntl FD_CLOEXEC"); 1974 | exit(1); 1975 | } 1976 | child = wl_client_create(dpy, fd[0]); 1977 | if (!child) { 1978 | perror("wl_client_create"); 1979 | exit(1); 1980 | } 1981 | child_destroy.notify = child_destroyed; 1982 | wl_client_add_destroy_listener(child, &child_destroy); 1983 | snprintf(fdstr, sizeof fdstr, "%d", fd[1]); 1984 | setenv("WAYLAND_SOCKET", fdstr, 1); 1985 | err = posix_spawnp(&pid, argv[0], NULL, NULL, argv, environ); 1986 | if (err) { 1987 | fprintf(stderr, "spawn %s: %s\n", argv[0], strerror(err)); 1988 | exit(1); 1989 | } 1990 | close(fd[1]); 1991 | } 1992 | 1993 | static int 1994 | fsready(int fd, uint32_t mask, void *ptr) 1995 | { 1996 | C9ctx *ctx; 1997 | 1998 | ctx = ptr; 1999 | if (mask & WL_EVENT_WRITABLE) 2000 | fswriteT(ctx); 2001 | if (mask & WL_EVENT_READABLE) 2002 | fsreadR(ctx); 2003 | return 0; 2004 | } 2005 | 2006 | int 2007 | main(int argc, char *argv[]) 2008 | { 2009 | static const struct { 2010 | const struct wl_interface *iface; 2011 | int ver; 2012 | wl_global_bind_func_t bind; 2013 | } *g, globals[] = { 2014 | {&wl_compositor_interface, 5, bind_compositor}, 2015 | {&wl_seat_interface, 5, bind_seat}, 2016 | {&wl_data_device_manager_interface, 3, bind_dataman}, 2017 | {&xdg_wm_base_interface, 3, bind_wm}, 2018 | {&wl_output_interface, 4, bind_output}, 2019 | {&org_kde_kwin_server_decoration_manager_interface, 1, bind_decoman}, 2020 | }; 2021 | struct wl_list *clients; 2022 | char *wsys, *err; 2023 | const char *wsyspath[C9maxpathel], *sock; 2024 | uint32_t mask; 2025 | 2026 | termaux.rfd = -1; 2027 | draw.datafd = -1; 2028 | ARGBEGIN { 2029 | case 't': 2030 | fdpair(EARGF(usage()), &termaux.rfd, &termaux.wfd); 2031 | break; 2032 | case 'd': 2033 | fdpair(EARGF(usage()), &draw.datafd, NULL); 2034 | break; 2035 | default: 2036 | usage(); 2037 | } ARGEND 2038 | 2039 | if (termaux.rfd < 0) { 2040 | termaux.rfd = open("/dev/virtio-ports/term", O_RDWR | O_CLOEXEC); 2041 | if (termaux.rfd < 0) { 2042 | perror("open /dev/virtio-ports/term"); 2043 | return 1; 2044 | } 2045 | termaux.wfd = termaux.rfd; 2046 | } 2047 | 2048 | fsinit(&termctx, &termaux); 2049 | fprintf(stderr, "9p msize %"PRIu32"\n", termctx.msize); 2050 | term.root = fsattach(&termctx, NULL); 2051 | if (term.root < 0) 2052 | return 1; 2053 | 2054 | wsys = termenv("wsys"); 2055 | if (!wsys) 2056 | return 1; 2057 | err = splitpath(wsys, wsyspath, LEN(wsyspath)); 2058 | if (err) { 2059 | fprintf(stderr, "invalid path '%s': %s\n", wsys, err); 2060 | return 1; 2061 | } 2062 | term.wsys = fswalk(&termctx, NULL, term.root, wsyspath); 2063 | if (term.wsys < 0) { 2064 | fprintf(stderr, "fswalk %s: %s\n", wsys, termaux.err); 2065 | return 1; 2066 | } 2067 | free(wsys); 2068 | 2069 | term.snarf = fswalk(&termctx, NULL, term.root, (const char *[]){"dev", "snarf", 0}); 2070 | if (term.snarf < 0) 2071 | return 1; 2072 | if (fsopen(&termctx, NULL, term.snarf, C9read) != 0) 2073 | return 1; 2074 | 2075 | if (drawinit(&termctx) != 0) 2076 | return 1; 2077 | if (keymapinit(&termctx) != 0) 2078 | return 1; 2079 | wl_list_init(&mouse.active); 2080 | wl_list_init(&mouse.inactive); 2081 | wl_list_init(&kbd.active); 2082 | wl_list_init(&kbd.inactive); 2083 | 2084 | dpy = wl_display_create(); 2085 | if (!dpy) { 2086 | perror("wl_display_create"); 2087 | return 1; 2088 | } 2089 | evt = wl_display_get_event_loop(dpy); 2090 | term.event = wl_event_loop_add_fd(evt, termaux.rfd, WL_EVENT_READABLE, fsready, &termctx); 2091 | if (!term.event) { 2092 | fprintf(stderr, "failed to add 9p event source\n"); 2093 | return 1; 2094 | } 2095 | sock = wl_display_add_socket_auto(dpy); 2096 | if (!sock) { 2097 | fprintf(stderr, "failed to add socket\n"); 2098 | return 1; 2099 | } 2100 | setenv("WAYLAND_DISPLAY", sock, 1); 2101 | if (wl_display_init_shm(dpy) != 0) { 2102 | fprintf(stderr, "failed to init shm\n"); 2103 | return 1; 2104 | } 2105 | for (g = globals; g < globals + LEN(globals); g++) { 2106 | if (!wl_global_create(dpy, g->iface, g->ver, NULL, g->bind)) { 2107 | fprintf(stderr, "wl_global_create %s failed\n", g->iface->name); 2108 | return 1; 2109 | } 2110 | } 2111 | wl_list_init(&output.resources); 2112 | wl_list_init(&datadev.resources); 2113 | 2114 | if (argc) 2115 | launch(argv); 2116 | clients = wl_display_get_client_list(dpy); 2117 | while (!argc || !wl_list_empty(clients)) { 2118 | wl_display_flush_clients(dpy); 2119 | wl_event_loop_dispatch(evt, -1); 2120 | fsdispatch(&termctx); 2121 | mask = WL_EVENT_READABLE; 2122 | if (termaux.wend > termaux.wpos) 2123 | mask |= WL_EVENT_WRITABLE; 2124 | if (mask != term.eventmask) 2125 | wl_event_source_fd_update(term.event, mask); 2126 | } 2127 | } 2128 | -------------------------------------------------------------------------------- /xdg-shell-server-protocol.h: -------------------------------------------------------------------------------- 1 | /* Generated by wayland-scanner 1.17.0 */ 2 | 3 | #ifndef XDG_SHELL_SERVER_PROTOCOL_H 4 | #define XDG_SHELL_SERVER_PROTOCOL_H 5 | 6 | #include 7 | #include 8 | #include "wayland-server.h" 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | struct wl_client; 15 | struct wl_resource; 16 | 17 | /** 18 | * @page page_xdg_shell The xdg_shell protocol 19 | * @section page_ifaces_xdg_shell Interfaces 20 | * - @subpage page_iface_xdg_wm_base - create desktop-style surfaces 21 | * - @subpage page_iface_xdg_positioner - child surface positioner 22 | * - @subpage page_iface_xdg_surface - desktop user interface surface base interface 23 | * - @subpage page_iface_xdg_toplevel - toplevel surface 24 | * - @subpage page_iface_xdg_popup - short-lived, popup surfaces for menus 25 | * @section page_copyright_xdg_shell Copyright 26 | *
  27 |  *
  28 |  * Copyright © 2008-2013 Kristian Høgsberg
  29 |  * Copyright © 2013      Rafael Antognolli
  30 |  * Copyright © 2013      Jasper St. Pierre
  31 |  * Copyright © 2010-2013 Intel Corporation
  32 |  * Copyright © 2015-2017 Samsung Electronics Co., Ltd
  33 |  * Copyright © 2015-2017 Red Hat Inc.
  34 |  *
  35 |  * Permission is hereby granted, free of charge, to any person obtaining a
  36 |  * copy of this software and associated documentation files (the "Software"),
  37 |  * to deal in the Software without restriction, including without limitation
  38 |  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  39 |  * and/or sell copies of the Software, and to permit persons to whom the
  40 |  * Software is furnished to do so, subject to the following conditions:
  41 |  *
  42 |  * The above copyright notice and this permission notice (including the next
  43 |  * paragraph) shall be included in all copies or substantial portions of the
  44 |  * Software.
  45 |  *
  46 |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  47 |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  48 |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  49 |  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  50 |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  51 |  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  52 |  * DEALINGS IN THE SOFTWARE.
  53 |  * 
54 | */ 55 | struct wl_output; 56 | struct wl_seat; 57 | struct wl_surface; 58 | struct xdg_popup; 59 | struct xdg_positioner; 60 | struct xdg_surface; 61 | struct xdg_toplevel; 62 | struct xdg_wm_base; 63 | 64 | #ifndef XDG_WM_BASE_INTERFACE 65 | #define XDG_WM_BASE_INTERFACE 66 | /** 67 | * @page page_iface_xdg_wm_base xdg_wm_base 68 | * @section page_iface_xdg_wm_base_desc Description 69 | * 70 | * The xdg_wm_base interface is exposed as a global object enabling clients 71 | * to turn their wl_surfaces into windows in a desktop environment. It 72 | * defines the basic functionality needed for clients and the compositor to 73 | * create windows that can be dragged, resized, maximized, etc, as well as 74 | * creating transient windows such as popup menus. 75 | * @section page_iface_xdg_wm_base_api API 76 | * See @ref iface_xdg_wm_base. 77 | */ 78 | /** 79 | * @defgroup iface_xdg_wm_base The xdg_wm_base interface 80 | * 81 | * The xdg_wm_base interface is exposed as a global object enabling clients 82 | * to turn their wl_surfaces into windows in a desktop environment. It 83 | * defines the basic functionality needed for clients and the compositor to 84 | * create windows that can be dragged, resized, maximized, etc, as well as 85 | * creating transient windows such as popup menus. 86 | */ 87 | extern const struct wl_interface xdg_wm_base_interface; 88 | #endif 89 | #ifndef XDG_POSITIONER_INTERFACE 90 | #define XDG_POSITIONER_INTERFACE 91 | /** 92 | * @page page_iface_xdg_positioner xdg_positioner 93 | * @section page_iface_xdg_positioner_desc Description 94 | * 95 | * The xdg_positioner provides a collection of rules for the placement of a 96 | * child surface relative to a parent surface. Rules can be defined to ensure 97 | * the child surface remains within the visible area's borders, and to 98 | * specify how the child surface changes its position, such as sliding along 99 | * an axis, or flipping around a rectangle. These positioner-created rules are 100 | * constrained by the requirement that a child surface must intersect with or 101 | * be at least partially adjacent to its parent surface. 102 | * 103 | * See the various requests for details about possible rules. 104 | * 105 | * At the time of the request, the compositor makes a copy of the rules 106 | * specified by the xdg_positioner. Thus, after the request is complete the 107 | * xdg_positioner object can be destroyed or reused; further changes to the 108 | * object will have no effect on previous usages. 109 | * 110 | * For an xdg_positioner object to be considered complete, it must have a 111 | * non-zero size set by set_size, and a non-zero anchor rectangle set by 112 | * set_anchor_rect. Passing an incomplete xdg_positioner object when 113 | * positioning a surface raises an error. 114 | * @section page_iface_xdg_positioner_api API 115 | * See @ref iface_xdg_positioner. 116 | */ 117 | /** 118 | * @defgroup iface_xdg_positioner The xdg_positioner interface 119 | * 120 | * The xdg_positioner provides a collection of rules for the placement of a 121 | * child surface relative to a parent surface. Rules can be defined to ensure 122 | * the child surface remains within the visible area's borders, and to 123 | * specify how the child surface changes its position, such as sliding along 124 | * an axis, or flipping around a rectangle. These positioner-created rules are 125 | * constrained by the requirement that a child surface must intersect with or 126 | * be at least partially adjacent to its parent surface. 127 | * 128 | * See the various requests for details about possible rules. 129 | * 130 | * At the time of the request, the compositor makes a copy of the rules 131 | * specified by the xdg_positioner. Thus, after the request is complete the 132 | * xdg_positioner object can be destroyed or reused; further changes to the 133 | * object will have no effect on previous usages. 134 | * 135 | * For an xdg_positioner object to be considered complete, it must have a 136 | * non-zero size set by set_size, and a non-zero anchor rectangle set by 137 | * set_anchor_rect. Passing an incomplete xdg_positioner object when 138 | * positioning a surface raises an error. 139 | */ 140 | extern const struct wl_interface xdg_positioner_interface; 141 | #endif 142 | #ifndef XDG_SURFACE_INTERFACE 143 | #define XDG_SURFACE_INTERFACE 144 | /** 145 | * @page page_iface_xdg_surface xdg_surface 146 | * @section page_iface_xdg_surface_desc Description 147 | * 148 | * An interface that may be implemented by a wl_surface, for 149 | * implementations that provide a desktop-style user interface. 150 | * 151 | * It provides a base set of functionality required to construct user 152 | * interface elements requiring management by the compositor, such as 153 | * toplevel windows, menus, etc. The types of functionality are split into 154 | * xdg_surface roles. 155 | * 156 | * Creating an xdg_surface does not set the role for a wl_surface. In order 157 | * to map an xdg_surface, the client must create a role-specific object 158 | * using, e.g., get_toplevel, get_popup. The wl_surface for any given 159 | * xdg_surface can have at most one role, and may not be assigned any role 160 | * not based on xdg_surface. 161 | * 162 | * A role must be assigned before any other requests are made to the 163 | * xdg_surface object. 164 | * 165 | * The client must call wl_surface.commit on the corresponding wl_surface 166 | * for the xdg_surface state to take effect. 167 | * 168 | * Creating an xdg_surface from a wl_surface which has a buffer attached or 169 | * committed is a client error, and any attempts by a client to attach or 170 | * manipulate a buffer prior to the first xdg_surface.configure call must 171 | * also be treated as errors. 172 | * 173 | * After creating a role-specific object and setting it up, the client must 174 | * perform an initial commit without any buffer attached. The compositor 175 | * will reply with an xdg_surface.configure event. The client must 176 | * acknowledge it and is then allowed to attach a buffer to map the surface. 177 | * 178 | * Mapping an xdg_surface-based role surface is defined as making it 179 | * possible for the surface to be shown by the compositor. Note that 180 | * a mapped surface is not guaranteed to be visible once it is mapped. 181 | * 182 | * For an xdg_surface to be mapped by the compositor, the following 183 | * conditions must be met: 184 | * (1) the client has assigned an xdg_surface-based role to the surface 185 | * (2) the client has set and committed the xdg_surface state and the 186 | * role-dependent state to the surface 187 | * (3) the client has committed a buffer to the surface 188 | * 189 | * A newly-unmapped surface is considered to have met condition (1) out 190 | * of the 3 required conditions for mapping a surface if its role surface 191 | * has not been destroyed. 192 | * @section page_iface_xdg_surface_api API 193 | * See @ref iface_xdg_surface. 194 | */ 195 | /** 196 | * @defgroup iface_xdg_surface The xdg_surface interface 197 | * 198 | * An interface that may be implemented by a wl_surface, for 199 | * implementations that provide a desktop-style user interface. 200 | * 201 | * It provides a base set of functionality required to construct user 202 | * interface elements requiring management by the compositor, such as 203 | * toplevel windows, menus, etc. The types of functionality are split into 204 | * xdg_surface roles. 205 | * 206 | * Creating an xdg_surface does not set the role for a wl_surface. In order 207 | * to map an xdg_surface, the client must create a role-specific object 208 | * using, e.g., get_toplevel, get_popup. The wl_surface for any given 209 | * xdg_surface can have at most one role, and may not be assigned any role 210 | * not based on xdg_surface. 211 | * 212 | * A role must be assigned before any other requests are made to the 213 | * xdg_surface object. 214 | * 215 | * The client must call wl_surface.commit on the corresponding wl_surface 216 | * for the xdg_surface state to take effect. 217 | * 218 | * Creating an xdg_surface from a wl_surface which has a buffer attached or 219 | * committed is a client error, and any attempts by a client to attach or 220 | * manipulate a buffer prior to the first xdg_surface.configure call must 221 | * also be treated as errors. 222 | * 223 | * After creating a role-specific object and setting it up, the client must 224 | * perform an initial commit without any buffer attached. The compositor 225 | * will reply with an xdg_surface.configure event. The client must 226 | * acknowledge it and is then allowed to attach a buffer to map the surface. 227 | * 228 | * Mapping an xdg_surface-based role surface is defined as making it 229 | * possible for the surface to be shown by the compositor. Note that 230 | * a mapped surface is not guaranteed to be visible once it is mapped. 231 | * 232 | * For an xdg_surface to be mapped by the compositor, the following 233 | * conditions must be met: 234 | * (1) the client has assigned an xdg_surface-based role to the surface 235 | * (2) the client has set and committed the xdg_surface state and the 236 | * role-dependent state to the surface 237 | * (3) the client has committed a buffer to the surface 238 | * 239 | * A newly-unmapped surface is considered to have met condition (1) out 240 | * of the 3 required conditions for mapping a surface if its role surface 241 | * has not been destroyed. 242 | */ 243 | extern const struct wl_interface xdg_surface_interface; 244 | #endif 245 | #ifndef XDG_TOPLEVEL_INTERFACE 246 | #define XDG_TOPLEVEL_INTERFACE 247 | /** 248 | * @page page_iface_xdg_toplevel xdg_toplevel 249 | * @section page_iface_xdg_toplevel_desc Description 250 | * 251 | * This interface defines an xdg_surface role which allows a surface to, 252 | * among other things, set window-like properties such as maximize, 253 | * fullscreen, and minimize, set application-specific metadata like title and 254 | * id, and well as trigger user interactive operations such as interactive 255 | * resize and move. 256 | * 257 | * Unmapping an xdg_toplevel means that the surface cannot be shown 258 | * by the compositor until it is explicitly mapped again. 259 | * All active operations (e.g., move, resize) are canceled and all 260 | * attributes (e.g. title, state, stacking, ...) are discarded for 261 | * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to 262 | * the state it had right after xdg_surface.get_toplevel. The client 263 | * can re-map the toplevel by perfoming a commit without any buffer 264 | * attached, waiting for a configure event and handling it as usual (see 265 | * xdg_surface description). 266 | * 267 | * Attaching a null buffer to a toplevel unmaps the surface. 268 | * @section page_iface_xdg_toplevel_api API 269 | * See @ref iface_xdg_toplevel. 270 | */ 271 | /** 272 | * @defgroup iface_xdg_toplevel The xdg_toplevel interface 273 | * 274 | * This interface defines an xdg_surface role which allows a surface to, 275 | * among other things, set window-like properties such as maximize, 276 | * fullscreen, and minimize, set application-specific metadata like title and 277 | * id, and well as trigger user interactive operations such as interactive 278 | * resize and move. 279 | * 280 | * Unmapping an xdg_toplevel means that the surface cannot be shown 281 | * by the compositor until it is explicitly mapped again. 282 | * All active operations (e.g., move, resize) are canceled and all 283 | * attributes (e.g. title, state, stacking, ...) are discarded for 284 | * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to 285 | * the state it had right after xdg_surface.get_toplevel. The client 286 | * can re-map the toplevel by perfoming a commit without any buffer 287 | * attached, waiting for a configure event and handling it as usual (see 288 | * xdg_surface description). 289 | * 290 | * Attaching a null buffer to a toplevel unmaps the surface. 291 | */ 292 | extern const struct wl_interface xdg_toplevel_interface; 293 | #endif 294 | #ifndef XDG_POPUP_INTERFACE 295 | #define XDG_POPUP_INTERFACE 296 | /** 297 | * @page page_iface_xdg_popup xdg_popup 298 | * @section page_iface_xdg_popup_desc Description 299 | * 300 | * A popup surface is a short-lived, temporary surface. It can be used to 301 | * implement for example menus, popovers, tooltips and other similar user 302 | * interface concepts. 303 | * 304 | * A popup can be made to take an explicit grab. See xdg_popup.grab for 305 | * details. 306 | * 307 | * When the popup is dismissed, a popup_done event will be sent out, and at 308 | * the same time the surface will be unmapped. See the xdg_popup.popup_done 309 | * event for details. 310 | * 311 | * Explicitly destroying the xdg_popup object will also dismiss the popup and 312 | * unmap the surface. Clients that want to dismiss the popup when another 313 | * surface of their own is clicked should dismiss the popup using the destroy 314 | * request. 315 | * 316 | * A newly created xdg_popup will be stacked on top of all previously created 317 | * xdg_popup surfaces associated with the same xdg_toplevel. 318 | * 319 | * The parent of an xdg_popup must be mapped (see the xdg_surface 320 | * description) before the xdg_popup itself. 321 | * 322 | * The client must call wl_surface.commit on the corresponding wl_surface 323 | * for the xdg_popup state to take effect. 324 | * @section page_iface_xdg_popup_api API 325 | * See @ref iface_xdg_popup. 326 | */ 327 | /** 328 | * @defgroup iface_xdg_popup The xdg_popup interface 329 | * 330 | * A popup surface is a short-lived, temporary surface. It can be used to 331 | * implement for example menus, popovers, tooltips and other similar user 332 | * interface concepts. 333 | * 334 | * A popup can be made to take an explicit grab. See xdg_popup.grab for 335 | * details. 336 | * 337 | * When the popup is dismissed, a popup_done event will be sent out, and at 338 | * the same time the surface will be unmapped. See the xdg_popup.popup_done 339 | * event for details. 340 | * 341 | * Explicitly destroying the xdg_popup object will also dismiss the popup and 342 | * unmap the surface. Clients that want to dismiss the popup when another 343 | * surface of their own is clicked should dismiss the popup using the destroy 344 | * request. 345 | * 346 | * A newly created xdg_popup will be stacked on top of all previously created 347 | * xdg_popup surfaces associated with the same xdg_toplevel. 348 | * 349 | * The parent of an xdg_popup must be mapped (see the xdg_surface 350 | * description) before the xdg_popup itself. 351 | * 352 | * The client must call wl_surface.commit on the corresponding wl_surface 353 | * for the xdg_popup state to take effect. 354 | */ 355 | extern const struct wl_interface xdg_popup_interface; 356 | #endif 357 | 358 | #ifndef XDG_WM_BASE_ERROR_ENUM 359 | #define XDG_WM_BASE_ERROR_ENUM 360 | enum xdg_wm_base_error { 361 | /** 362 | * given wl_surface has another role 363 | */ 364 | XDG_WM_BASE_ERROR_ROLE = 0, 365 | /** 366 | * xdg_wm_base was destroyed before children 367 | */ 368 | XDG_WM_BASE_ERROR_DEFUNCT_SURFACES = 1, 369 | /** 370 | * the client tried to map or destroy a non-topmost popup 371 | */ 372 | XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP = 2, 373 | /** 374 | * the client specified an invalid popup parent surface 375 | */ 376 | XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT = 3, 377 | /** 378 | * the client provided an invalid surface state 379 | */ 380 | XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE = 4, 381 | /** 382 | * the client provided an invalid positioner 383 | */ 384 | XDG_WM_BASE_ERROR_INVALID_POSITIONER = 5, 385 | }; 386 | #endif /* XDG_WM_BASE_ERROR_ENUM */ 387 | 388 | /** 389 | * @ingroup iface_xdg_wm_base 390 | * @struct xdg_wm_base_interface 391 | */ 392 | struct xdg_wm_base_interface { 393 | /** 394 | * destroy xdg_wm_base 395 | * 396 | * Destroy this xdg_wm_base object. 397 | * 398 | * Destroying a bound xdg_wm_base object while there are surfaces 399 | * still alive created by this xdg_wm_base object instance is 400 | * illegal and will result in a protocol error. 401 | */ 402 | void (*destroy)(struct wl_client *client, 403 | struct wl_resource *resource); 404 | /** 405 | * create a positioner object 406 | * 407 | * Create a positioner object. A positioner object is used to 408 | * position surfaces relative to some parent surface. See the 409 | * interface description and xdg_surface.get_popup for details. 410 | */ 411 | void (*create_positioner)(struct wl_client *client, 412 | struct wl_resource *resource, 413 | uint32_t id); 414 | /** 415 | * create a shell surface from a surface 416 | * 417 | * This creates an xdg_surface for the given surface. While 418 | * xdg_surface itself is not a role, the corresponding surface may 419 | * only be assigned a role extending xdg_surface, such as 420 | * xdg_toplevel or xdg_popup. 421 | * 422 | * This creates an xdg_surface for the given surface. An 423 | * xdg_surface is used as basis to define a role to a given 424 | * surface, such as xdg_toplevel or xdg_popup. It also manages 425 | * functionality shared between xdg_surface based surface roles. 426 | * 427 | * See the documentation of xdg_surface for more details about what 428 | * an xdg_surface is and how it is used. 429 | */ 430 | void (*get_xdg_surface)(struct wl_client *client, 431 | struct wl_resource *resource, 432 | uint32_t id, 433 | struct wl_resource *surface); 434 | /** 435 | * respond to a ping event 436 | * 437 | * A client must respond to a ping event with a pong request or 438 | * the client may be deemed unresponsive. See xdg_wm_base.ping. 439 | * @param serial serial of the ping event 440 | */ 441 | void (*pong)(struct wl_client *client, 442 | struct wl_resource *resource, 443 | uint32_t serial); 444 | }; 445 | 446 | #define XDG_WM_BASE_PING 0 447 | 448 | /** 449 | * @ingroup iface_xdg_wm_base 450 | */ 451 | #define XDG_WM_BASE_PING_SINCE_VERSION 1 452 | 453 | /** 454 | * @ingroup iface_xdg_wm_base 455 | */ 456 | #define XDG_WM_BASE_DESTROY_SINCE_VERSION 1 457 | /** 458 | * @ingroup iface_xdg_wm_base 459 | */ 460 | #define XDG_WM_BASE_CREATE_POSITIONER_SINCE_VERSION 1 461 | /** 462 | * @ingroup iface_xdg_wm_base 463 | */ 464 | #define XDG_WM_BASE_GET_XDG_SURFACE_SINCE_VERSION 1 465 | /** 466 | * @ingroup iface_xdg_wm_base 467 | */ 468 | #define XDG_WM_BASE_PONG_SINCE_VERSION 1 469 | 470 | /** 471 | * @ingroup iface_xdg_wm_base 472 | * Sends an ping event to the client owning the resource. 473 | * @param resource_ The client's resource 474 | * @param serial pass this to the pong request 475 | */ 476 | static inline void 477 | xdg_wm_base_send_ping(struct wl_resource *resource_, uint32_t serial) 478 | { 479 | wl_resource_post_event(resource_, XDG_WM_BASE_PING, serial); 480 | } 481 | 482 | #ifndef XDG_POSITIONER_ERROR_ENUM 483 | #define XDG_POSITIONER_ERROR_ENUM 484 | enum xdg_positioner_error { 485 | /** 486 | * invalid input provided 487 | */ 488 | XDG_POSITIONER_ERROR_INVALID_INPUT = 0, 489 | }; 490 | #endif /* XDG_POSITIONER_ERROR_ENUM */ 491 | 492 | #ifndef XDG_POSITIONER_ANCHOR_ENUM 493 | #define XDG_POSITIONER_ANCHOR_ENUM 494 | enum xdg_positioner_anchor { 495 | XDG_POSITIONER_ANCHOR_NONE = 0, 496 | XDG_POSITIONER_ANCHOR_TOP = 1, 497 | XDG_POSITIONER_ANCHOR_BOTTOM = 2, 498 | XDG_POSITIONER_ANCHOR_LEFT = 3, 499 | XDG_POSITIONER_ANCHOR_RIGHT = 4, 500 | XDG_POSITIONER_ANCHOR_TOP_LEFT = 5, 501 | XDG_POSITIONER_ANCHOR_BOTTOM_LEFT = 6, 502 | XDG_POSITIONER_ANCHOR_TOP_RIGHT = 7, 503 | XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT = 8, 504 | }; 505 | #endif /* XDG_POSITIONER_ANCHOR_ENUM */ 506 | 507 | #ifndef XDG_POSITIONER_GRAVITY_ENUM 508 | #define XDG_POSITIONER_GRAVITY_ENUM 509 | enum xdg_positioner_gravity { 510 | XDG_POSITIONER_GRAVITY_NONE = 0, 511 | XDG_POSITIONER_GRAVITY_TOP = 1, 512 | XDG_POSITIONER_GRAVITY_BOTTOM = 2, 513 | XDG_POSITIONER_GRAVITY_LEFT = 3, 514 | XDG_POSITIONER_GRAVITY_RIGHT = 4, 515 | XDG_POSITIONER_GRAVITY_TOP_LEFT = 5, 516 | XDG_POSITIONER_GRAVITY_BOTTOM_LEFT = 6, 517 | XDG_POSITIONER_GRAVITY_TOP_RIGHT = 7, 518 | XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT = 8, 519 | }; 520 | #endif /* XDG_POSITIONER_GRAVITY_ENUM */ 521 | 522 | #ifndef XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM 523 | #define XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM 524 | /** 525 | * @ingroup iface_xdg_positioner 526 | * vertically resize the surface 527 | * 528 | * Resize the surface vertically so that it is completely unconstrained. 529 | */ 530 | enum xdg_positioner_constraint_adjustment { 531 | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE = 0, 532 | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1, 533 | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 2, 534 | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X = 4, 535 | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y = 8, 536 | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X = 16, 537 | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 32, 538 | }; 539 | #endif /* XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM */ 540 | 541 | /** 542 | * @ingroup iface_xdg_positioner 543 | * @struct xdg_positioner_interface 544 | */ 545 | struct xdg_positioner_interface { 546 | /** 547 | * destroy the xdg_positioner object 548 | * 549 | * Notify the compositor that the xdg_positioner will no longer 550 | * be used. 551 | */ 552 | void (*destroy)(struct wl_client *client, 553 | struct wl_resource *resource); 554 | /** 555 | * set the size of the to-be positioned rectangle 556 | * 557 | * Set the size of the surface that is to be positioned with the 558 | * positioner object. The size is in surface-local coordinates and 559 | * corresponds to the window geometry. See 560 | * xdg_surface.set_window_geometry. 561 | * 562 | * If a zero or negative size is set the invalid_input error is 563 | * raised. 564 | * @param width width of positioned rectangle 565 | * @param height height of positioned rectangle 566 | */ 567 | void (*set_size)(struct wl_client *client, 568 | struct wl_resource *resource, 569 | int32_t width, 570 | int32_t height); 571 | /** 572 | * set the anchor rectangle within the parent surface 573 | * 574 | * Specify the anchor rectangle within the parent surface that 575 | * the child surface will be placed relative to. The rectangle is 576 | * relative to the window geometry as defined by 577 | * xdg_surface.set_window_geometry of the parent surface. 578 | * 579 | * When the xdg_positioner object is used to position a child 580 | * surface, the anchor rectangle may not extend outside the window 581 | * geometry of the positioned child's parent surface. 582 | * 583 | * If a negative size is set the invalid_input error is raised. 584 | * @param x x position of anchor rectangle 585 | * @param y y position of anchor rectangle 586 | * @param width width of anchor rectangle 587 | * @param height height of anchor rectangle 588 | */ 589 | void (*set_anchor_rect)(struct wl_client *client, 590 | struct wl_resource *resource, 591 | int32_t x, 592 | int32_t y, 593 | int32_t width, 594 | int32_t height); 595 | /** 596 | * set anchor rectangle anchor 597 | * 598 | * Defines the anchor point for the anchor rectangle. The 599 | * specified anchor is used derive an anchor point that the child 600 | * surface will be positioned relative to. If a corner anchor is 601 | * set (e.g. 'top_left' or 'bottom_right'), the anchor point will 602 | * be at the specified corner; otherwise, the derived anchor point 603 | * will be centered on the specified edge, or in the center of the 604 | * anchor rectangle if no edge is specified. 605 | * @param anchor anchor 606 | */ 607 | void (*set_anchor)(struct wl_client *client, 608 | struct wl_resource *resource, 609 | uint32_t anchor); 610 | /** 611 | * set child surface gravity 612 | * 613 | * Defines in what direction a surface should be positioned, 614 | * relative to the anchor point of the parent surface. If a corner 615 | * gravity is specified (e.g. 'bottom_right' or 'top_left'), then 616 | * the child surface will be placed towards the specified gravity; 617 | * otherwise, the child surface will be centered over the anchor 618 | * point on any axis that had no gravity specified. 619 | * @param gravity gravity direction 620 | */ 621 | void (*set_gravity)(struct wl_client *client, 622 | struct wl_resource *resource, 623 | uint32_t gravity); 624 | /** 625 | * set the adjustment to be done when constrained 626 | * 627 | * Specify how the window should be positioned if the originally 628 | * intended position caused the surface to be constrained, meaning 629 | * at least partially outside positioning boundaries set by the 630 | * compositor. The adjustment is set by constructing a bitmask 631 | * describing the adjustment to be made when the surface is 632 | * constrained on that axis. 633 | * 634 | * If no bit for one axis is set, the compositor will assume that 635 | * the child surface should not change its position on that axis 636 | * when constrained. 637 | * 638 | * If more than one bit for one axis is set, the order of how 639 | * adjustments are applied is specified in the corresponding 640 | * adjustment descriptions. 641 | * 642 | * The default adjustment is none. 643 | * @param constraint_adjustment bit mask of constraint adjustments 644 | */ 645 | void (*set_constraint_adjustment)(struct wl_client *client, 646 | struct wl_resource *resource, 647 | uint32_t constraint_adjustment); 648 | /** 649 | * set surface position offset 650 | * 651 | * Specify the surface position offset relative to the position 652 | * of the anchor on the anchor rectangle and the anchor on the 653 | * surface. For example if the anchor of the anchor rectangle is at 654 | * (x, y), the surface has the gravity bottom|right, and the offset 655 | * is (ox, oy), the calculated surface position will be (x + ox, y 656 | * + oy). The offset position of the surface is the one used for 657 | * constraint testing. See set_constraint_adjustment. 658 | * 659 | * An example use case is placing a popup menu on top of a user 660 | * interface element, while aligning the user interface element of 661 | * the parent surface with some user interface element placed 662 | * somewhere in the popup surface. 663 | * @param x surface position x offset 664 | * @param y surface position y offset 665 | */ 666 | void (*set_offset)(struct wl_client *client, 667 | struct wl_resource *resource, 668 | int32_t x, 669 | int32_t y); 670 | /** 671 | * continuously reconstrain the surface 672 | * 673 | * When set reactive, the surface is reconstrained if the 674 | * conditions used for constraining changed, e.g. the parent window 675 | * moved. 676 | * 677 | * If the conditions changed and the popup was reconstrained, an 678 | * xdg_popup.configure event is sent with updated geometry, 679 | * followed by an xdg_surface.configure event. 680 | * @since 3 681 | */ 682 | void (*set_reactive)(struct wl_client *client, 683 | struct wl_resource *resource); 684 | /** 685 | * 686 | * 687 | * Set the parent window geometry the compositor should use when 688 | * positioning the popup. The compositor may use this information 689 | * to determine the future state the popup should be constrained 690 | * using. If this doesn't match the dimension of the parent the 691 | * popup is eventually positioned against, the behavior is 692 | * undefined. 693 | * 694 | * The arguments are given in the surface-local coordinate space. 695 | * @param parent_width future window geometry width of parent 696 | * @param parent_height future window geometry height of parent 697 | * @since 3 698 | */ 699 | void (*set_parent_size)(struct wl_client *client, 700 | struct wl_resource *resource, 701 | int32_t parent_width, 702 | int32_t parent_height); 703 | /** 704 | * set parent configure this is a response to 705 | * 706 | * Set the serial of an xdg_surface.configure event this 707 | * positioner will be used in response to. The compositor may use 708 | * this information together with set_parent_size to determine what 709 | * future state the popup should be constrained using. 710 | * @param serial serial of parent configure event 711 | * @since 3 712 | */ 713 | void (*set_parent_configure)(struct wl_client *client, 714 | struct wl_resource *resource, 715 | uint32_t serial); 716 | }; 717 | 718 | 719 | /** 720 | * @ingroup iface_xdg_positioner 721 | */ 722 | #define XDG_POSITIONER_DESTROY_SINCE_VERSION 1 723 | /** 724 | * @ingroup iface_xdg_positioner 725 | */ 726 | #define XDG_POSITIONER_SET_SIZE_SINCE_VERSION 1 727 | /** 728 | * @ingroup iface_xdg_positioner 729 | */ 730 | #define XDG_POSITIONER_SET_ANCHOR_RECT_SINCE_VERSION 1 731 | /** 732 | * @ingroup iface_xdg_positioner 733 | */ 734 | #define XDG_POSITIONER_SET_ANCHOR_SINCE_VERSION 1 735 | /** 736 | * @ingroup iface_xdg_positioner 737 | */ 738 | #define XDG_POSITIONER_SET_GRAVITY_SINCE_VERSION 1 739 | /** 740 | * @ingroup iface_xdg_positioner 741 | */ 742 | #define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT_SINCE_VERSION 1 743 | /** 744 | * @ingroup iface_xdg_positioner 745 | */ 746 | #define XDG_POSITIONER_SET_OFFSET_SINCE_VERSION 1 747 | /** 748 | * @ingroup iface_xdg_positioner 749 | */ 750 | #define XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION 3 751 | /** 752 | * @ingroup iface_xdg_positioner 753 | */ 754 | #define XDG_POSITIONER_SET_PARENT_SIZE_SINCE_VERSION 3 755 | /** 756 | * @ingroup iface_xdg_positioner 757 | */ 758 | #define XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION 3 759 | 760 | #ifndef XDG_SURFACE_ERROR_ENUM 761 | #define XDG_SURFACE_ERROR_ENUM 762 | enum xdg_surface_error { 763 | XDG_SURFACE_ERROR_NOT_CONSTRUCTED = 1, 764 | XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED = 2, 765 | XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER = 3, 766 | }; 767 | #endif /* XDG_SURFACE_ERROR_ENUM */ 768 | 769 | /** 770 | * @ingroup iface_xdg_surface 771 | * @struct xdg_surface_interface 772 | */ 773 | struct xdg_surface_interface { 774 | /** 775 | * destroy the xdg_surface 776 | * 777 | * Destroy the xdg_surface object. An xdg_surface must only be 778 | * destroyed after its role object has been destroyed. 779 | */ 780 | void (*destroy)(struct wl_client *client, 781 | struct wl_resource *resource); 782 | /** 783 | * assign the xdg_toplevel surface role 784 | * 785 | * This creates an xdg_toplevel object for the given xdg_surface 786 | * and gives the associated wl_surface the xdg_toplevel role. 787 | * 788 | * See the documentation of xdg_toplevel for more details about 789 | * what an xdg_toplevel is and how it is used. 790 | */ 791 | void (*get_toplevel)(struct wl_client *client, 792 | struct wl_resource *resource, 793 | uint32_t id); 794 | /** 795 | * assign the xdg_popup surface role 796 | * 797 | * This creates an xdg_popup object for the given xdg_surface and 798 | * gives the associated wl_surface the xdg_popup role. 799 | * 800 | * If null is passed as a parent, a parent surface must be 801 | * specified using some other protocol, before committing the 802 | * initial state. 803 | * 804 | * See the documentation of xdg_popup for more details about what 805 | * an xdg_popup is and how it is used. 806 | */ 807 | void (*get_popup)(struct wl_client *client, 808 | struct wl_resource *resource, 809 | uint32_t id, 810 | struct wl_resource *parent, 811 | struct wl_resource *positioner); 812 | /** 813 | * set the new window geometry 814 | * 815 | * The window geometry of a surface is its "visible bounds" from 816 | * the user's perspective. Client-side decorations often have 817 | * invisible portions like drop-shadows which should be ignored for 818 | * the purposes of aligning, placing and constraining windows. 819 | * 820 | * The window geometry is double buffered, and will be applied at 821 | * the time wl_surface.commit of the corresponding wl_surface is 822 | * called. 823 | * 824 | * When maintaining a position, the compositor should treat the (x, 825 | * y) coordinate of the window geometry as the top left corner of 826 | * the window. A client changing the (x, y) window geometry 827 | * coordinate should in general not alter the position of the 828 | * window. 829 | * 830 | * Once the window geometry of the surface is set, it is not 831 | * possible to unset it, and it will remain the same until 832 | * set_window_geometry is called again, even if a new subsurface or 833 | * buffer is attached. 834 | * 835 | * If never set, the value is the full bounds of the surface, 836 | * including any subsurfaces. This updates dynamically on every 837 | * commit. This unset is meant for extremely simple clients. 838 | * 839 | * The arguments are given in the surface-local coordinate space of 840 | * the wl_surface associated with this xdg_surface. 841 | * 842 | * The width and height must be greater than zero. Setting an 843 | * invalid size will raise an error. When applied, the effective 844 | * window geometry will be the set window geometry clamped to the 845 | * bounding rectangle of the combined geometry of the surface of 846 | * the xdg_surface and the associated subsurfaces. 847 | */ 848 | void (*set_window_geometry)(struct wl_client *client, 849 | struct wl_resource *resource, 850 | int32_t x, 851 | int32_t y, 852 | int32_t width, 853 | int32_t height); 854 | /** 855 | * ack a configure event 856 | * 857 | * When a configure event is received, if a client commits the 858 | * surface in response to the configure event, then the client must 859 | * make an ack_configure request sometime before the commit 860 | * request, passing along the serial of the configure event. 861 | * 862 | * For instance, for toplevel surfaces the compositor might use 863 | * this information to move a surface to the top left only when the 864 | * client has drawn itself for the maximized or fullscreen state. 865 | * 866 | * If the client receives multiple configure events before it can 867 | * respond to one, it only has to ack the last configure event. 868 | * 869 | * A client is not required to commit immediately after sending an 870 | * ack_configure request - it may even ack_configure several times 871 | * before its next surface commit. 872 | * 873 | * A client may send multiple ack_configure requests before 874 | * committing, but only the last request sent before a commit 875 | * indicates which configure event the client really is responding 876 | * to. 877 | * @param serial the serial from the configure event 878 | */ 879 | void (*ack_configure)(struct wl_client *client, 880 | struct wl_resource *resource, 881 | uint32_t serial); 882 | }; 883 | 884 | #define XDG_SURFACE_CONFIGURE 0 885 | 886 | /** 887 | * @ingroup iface_xdg_surface 888 | */ 889 | #define XDG_SURFACE_CONFIGURE_SINCE_VERSION 1 890 | 891 | /** 892 | * @ingroup iface_xdg_surface 893 | */ 894 | #define XDG_SURFACE_DESTROY_SINCE_VERSION 1 895 | /** 896 | * @ingroup iface_xdg_surface 897 | */ 898 | #define XDG_SURFACE_GET_TOPLEVEL_SINCE_VERSION 1 899 | /** 900 | * @ingroup iface_xdg_surface 901 | */ 902 | #define XDG_SURFACE_GET_POPUP_SINCE_VERSION 1 903 | /** 904 | * @ingroup iface_xdg_surface 905 | */ 906 | #define XDG_SURFACE_SET_WINDOW_GEOMETRY_SINCE_VERSION 1 907 | /** 908 | * @ingroup iface_xdg_surface 909 | */ 910 | #define XDG_SURFACE_ACK_CONFIGURE_SINCE_VERSION 1 911 | 912 | /** 913 | * @ingroup iface_xdg_surface 914 | * Sends an configure event to the client owning the resource. 915 | * @param resource_ The client's resource 916 | * @param serial serial of the configure event 917 | */ 918 | static inline void 919 | xdg_surface_send_configure(struct wl_resource *resource_, uint32_t serial) 920 | { 921 | wl_resource_post_event(resource_, XDG_SURFACE_CONFIGURE, serial); 922 | } 923 | 924 | #ifndef XDG_TOPLEVEL_RESIZE_EDGE_ENUM 925 | #define XDG_TOPLEVEL_RESIZE_EDGE_ENUM 926 | /** 927 | * @ingroup iface_xdg_toplevel 928 | * edge values for resizing 929 | * 930 | * These values are used to indicate which edge of a surface 931 | * is being dragged in a resize operation. 932 | */ 933 | enum xdg_toplevel_resize_edge { 934 | XDG_TOPLEVEL_RESIZE_EDGE_NONE = 0, 935 | XDG_TOPLEVEL_RESIZE_EDGE_TOP = 1, 936 | XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM = 2, 937 | XDG_TOPLEVEL_RESIZE_EDGE_LEFT = 4, 938 | XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT = 5, 939 | XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT = 6, 940 | XDG_TOPLEVEL_RESIZE_EDGE_RIGHT = 8, 941 | XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT = 9, 942 | XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT = 10, 943 | }; 944 | #endif /* XDG_TOPLEVEL_RESIZE_EDGE_ENUM */ 945 | 946 | #ifndef XDG_TOPLEVEL_STATE_ENUM 947 | #define XDG_TOPLEVEL_STATE_ENUM 948 | /** 949 | * @ingroup iface_xdg_toplevel 950 | * the surface is tiled 951 | * 952 | * The window is currently in a tiled layout and the bottom edge is 953 | * considered to be adjacent to another part of the tiling grid. 954 | */ 955 | enum xdg_toplevel_state { 956 | /** 957 | * the surface is maximized 958 | */ 959 | XDG_TOPLEVEL_STATE_MAXIMIZED = 1, 960 | /** 961 | * the surface is fullscreen 962 | */ 963 | XDG_TOPLEVEL_STATE_FULLSCREEN = 2, 964 | /** 965 | * the surface is being resized 966 | */ 967 | XDG_TOPLEVEL_STATE_RESIZING = 3, 968 | /** 969 | * the surface is now activated 970 | */ 971 | XDG_TOPLEVEL_STATE_ACTIVATED = 4, 972 | /** 973 | * @since 2 974 | */ 975 | XDG_TOPLEVEL_STATE_TILED_LEFT = 5, 976 | /** 977 | * @since 2 978 | */ 979 | XDG_TOPLEVEL_STATE_TILED_RIGHT = 6, 980 | /** 981 | * @since 2 982 | */ 983 | XDG_TOPLEVEL_STATE_TILED_TOP = 7, 984 | /** 985 | * @since 2 986 | */ 987 | XDG_TOPLEVEL_STATE_TILED_BOTTOM = 8, 988 | }; 989 | /** 990 | * @ingroup iface_xdg_toplevel 991 | */ 992 | #define XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION 2 993 | /** 994 | * @ingroup iface_xdg_toplevel 995 | */ 996 | #define XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION 2 997 | /** 998 | * @ingroup iface_xdg_toplevel 999 | */ 1000 | #define XDG_TOPLEVEL_STATE_TILED_TOP_SINCE_VERSION 2 1001 | /** 1002 | * @ingroup iface_xdg_toplevel 1003 | */ 1004 | #define XDG_TOPLEVEL_STATE_TILED_BOTTOM_SINCE_VERSION 2 1005 | #endif /* XDG_TOPLEVEL_STATE_ENUM */ 1006 | 1007 | /** 1008 | * @ingroup iface_xdg_toplevel 1009 | * @struct xdg_toplevel_interface 1010 | */ 1011 | struct xdg_toplevel_interface { 1012 | /** 1013 | * destroy the xdg_toplevel 1014 | * 1015 | * This request destroys the role surface and unmaps the surface; 1016 | * see "Unmapping" behavior in interface section for details. 1017 | */ 1018 | void (*destroy)(struct wl_client *client, 1019 | struct wl_resource *resource); 1020 | /** 1021 | * set the parent of this surface 1022 | * 1023 | * Set the "parent" of this surface. This surface should be 1024 | * stacked above the parent surface and all other ancestor 1025 | * surfaces. 1026 | * 1027 | * Parent windows should be set on dialogs, toolboxes, or other 1028 | * "auxiliary" surfaces, so that the parent is raised when the 1029 | * dialog is raised. 1030 | * 1031 | * Setting a null parent for a child window removes any 1032 | * parent-child relationship for the child. Setting a null parent 1033 | * for a window which currently has no parent is a no-op. 1034 | * 1035 | * If the parent is unmapped then its children are managed as 1036 | * though the parent of the now-unmapped parent has become the 1037 | * parent of this surface. If no parent exists for the now-unmapped 1038 | * parent then the children are managed as though they have no 1039 | * parent surface. 1040 | */ 1041 | void (*set_parent)(struct wl_client *client, 1042 | struct wl_resource *resource, 1043 | struct wl_resource *parent); 1044 | /** 1045 | * set surface title 1046 | * 1047 | * Set a short title for the surface. 1048 | * 1049 | * This string may be used to identify the surface in a task bar, 1050 | * window list, or other user interface elements provided by the 1051 | * compositor. 1052 | * 1053 | * The string must be encoded in UTF-8. 1054 | */ 1055 | void (*set_title)(struct wl_client *client, 1056 | struct wl_resource *resource, 1057 | const char *title); 1058 | /** 1059 | * set application ID 1060 | * 1061 | * Set an application identifier for the surface. 1062 | * 1063 | * The app ID identifies the general class of applications to which 1064 | * the surface belongs. The compositor can use this to group 1065 | * multiple surfaces together, or to determine how to launch a new 1066 | * application. 1067 | * 1068 | * For D-Bus activatable applications, the app ID is used as the 1069 | * D-Bus service name. 1070 | * 1071 | * The compositor shell will try to group application surfaces 1072 | * together by their app ID. As a best practice, it is suggested to 1073 | * select app ID's that match the basename of the application's 1074 | * .desktop file. For example, "org.freedesktop.FooViewer" where 1075 | * the .desktop file is "org.freedesktop.FooViewer.desktop". 1076 | * 1077 | * Like other properties, a set_app_id request can be sent after 1078 | * the xdg_toplevel has been mapped to update the property. 1079 | * 1080 | * See the desktop-entry specification [0] for more details on 1081 | * application identifiers and how they relate to well-known D-Bus 1082 | * names and .desktop files. 1083 | * 1084 | * [0] http://standards.freedesktop.org/desktop-entry-spec/ 1085 | */ 1086 | void (*set_app_id)(struct wl_client *client, 1087 | struct wl_resource *resource, 1088 | const char *app_id); 1089 | /** 1090 | * show the window menu 1091 | * 1092 | * Clients implementing client-side decorations might want to 1093 | * show a context menu when right-clicking on the decorations, 1094 | * giving the user a menu that they can use to maximize or minimize 1095 | * the window. 1096 | * 1097 | * This request asks the compositor to pop up such a window menu at 1098 | * the given position, relative to the local surface coordinates of 1099 | * the parent surface. There are no guarantees as to what menu 1100 | * items the window menu contains. 1101 | * 1102 | * This request must be used in response to some sort of user 1103 | * action like a button press, key press, or touch down event. 1104 | * @param seat the wl_seat of the user event 1105 | * @param serial the serial of the user event 1106 | * @param x the x position to pop up the window menu at 1107 | * @param y the y position to pop up the window menu at 1108 | */ 1109 | void (*show_window_menu)(struct wl_client *client, 1110 | struct wl_resource *resource, 1111 | struct wl_resource *seat, 1112 | uint32_t serial, 1113 | int32_t x, 1114 | int32_t y); 1115 | /** 1116 | * start an interactive move 1117 | * 1118 | * Start an interactive, user-driven move of the surface. 1119 | * 1120 | * This request must be used in response to some sort of user 1121 | * action like a button press, key press, or touch down event. The 1122 | * passed serial is used to determine the type of interactive move 1123 | * (touch, pointer, etc). 1124 | * 1125 | * The server may ignore move requests depending on the state of 1126 | * the surface (e.g. fullscreen or maximized), or if the passed 1127 | * serial is no longer valid. 1128 | * 1129 | * If triggered, the surface will lose the focus of the device 1130 | * (wl_pointer, wl_touch, etc) used for the move. It is up to the 1131 | * compositor to visually indicate that the move is taking place, 1132 | * such as updating a pointer cursor, during the move. There is no 1133 | * guarantee that the device focus will return when the move is 1134 | * completed. 1135 | * @param seat the wl_seat of the user event 1136 | * @param serial the serial of the user event 1137 | */ 1138 | void (*move)(struct wl_client *client, 1139 | struct wl_resource *resource, 1140 | struct wl_resource *seat, 1141 | uint32_t serial); 1142 | /** 1143 | * start an interactive resize 1144 | * 1145 | * Start a user-driven, interactive resize of the surface. 1146 | * 1147 | * This request must be used in response to some sort of user 1148 | * action like a button press, key press, or touch down event. The 1149 | * passed serial is used to determine the type of interactive 1150 | * resize (touch, pointer, etc). 1151 | * 1152 | * The server may ignore resize requests depending on the state of 1153 | * the surface (e.g. fullscreen or maximized). 1154 | * 1155 | * If triggered, the client will receive configure events with the 1156 | * "resize" state enum value and the expected sizes. See the 1157 | * "resize" enum value for more details about what is required. The 1158 | * client must also acknowledge configure events using 1159 | * "ack_configure". After the resize is completed, the client will 1160 | * receive another "configure" event without the resize state. 1161 | * 1162 | * If triggered, the surface also will lose the focus of the device 1163 | * (wl_pointer, wl_touch, etc) used for the resize. It is up to the 1164 | * compositor to visually indicate that the resize is taking place, 1165 | * such as updating a pointer cursor, during the resize. There is 1166 | * no guarantee that the device focus will return when the resize 1167 | * is completed. 1168 | * 1169 | * The edges parameter specifies how the surface should be resized, 1170 | * and is one of the values of the resize_edge enum. The compositor 1171 | * may use this information to update the surface position for 1172 | * example when dragging the top left corner. The compositor may 1173 | * also use this information to adapt its behavior, e.g. choose an 1174 | * appropriate cursor image. 1175 | * @param seat the wl_seat of the user event 1176 | * @param serial the serial of the user event 1177 | * @param edges which edge or corner is being dragged 1178 | */ 1179 | void (*resize)(struct wl_client *client, 1180 | struct wl_resource *resource, 1181 | struct wl_resource *seat, 1182 | uint32_t serial, 1183 | uint32_t edges); 1184 | /** 1185 | * set the maximum size 1186 | * 1187 | * Set a maximum size for the window. 1188 | * 1189 | * The client can specify a maximum size so that the compositor 1190 | * does not try to configure the window beyond this size. 1191 | * 1192 | * The width and height arguments are in window geometry 1193 | * coordinates. See xdg_surface.set_window_geometry. 1194 | * 1195 | * Values set in this way are double-buffered. They will get 1196 | * applied on the next commit. 1197 | * 1198 | * The compositor can use this information to allow or disallow 1199 | * different states like maximize or fullscreen and draw accurate 1200 | * animations. 1201 | * 1202 | * Similarly, a tiling window manager may use this information to 1203 | * place and resize client windows in a more effective way. 1204 | * 1205 | * The client should not rely on the compositor to obey the maximum 1206 | * size. The compositor may decide to ignore the values set by the 1207 | * client and request a larger size. 1208 | * 1209 | * If never set, or a value of zero in the request, means that the 1210 | * client has no expected maximum size in the given dimension. As a 1211 | * result, a client wishing to reset the maximum size to an 1212 | * unspecified state can use zero for width and height in the 1213 | * request. 1214 | * 1215 | * Requesting a maximum size to be smaller than the minimum size of 1216 | * a surface is illegal and will result in a protocol error. 1217 | * 1218 | * The width and height must be greater than or equal to zero. 1219 | * Using strictly negative values for width and height will result 1220 | * in a protocol error. 1221 | */ 1222 | void (*set_max_size)(struct wl_client *client, 1223 | struct wl_resource *resource, 1224 | int32_t width, 1225 | int32_t height); 1226 | /** 1227 | * set the minimum size 1228 | * 1229 | * Set a minimum size for the window. 1230 | * 1231 | * The client can specify a minimum size so that the compositor 1232 | * does not try to configure the window below this size. 1233 | * 1234 | * The width and height arguments are in window geometry 1235 | * coordinates. See xdg_surface.set_window_geometry. 1236 | * 1237 | * Values set in this way are double-buffered. They will get 1238 | * applied on the next commit. 1239 | * 1240 | * The compositor can use this information to allow or disallow 1241 | * different states like maximize or fullscreen and draw accurate 1242 | * animations. 1243 | * 1244 | * Similarly, a tiling window manager may use this information to 1245 | * place and resize client windows in a more effective way. 1246 | * 1247 | * The client should not rely on the compositor to obey the minimum 1248 | * size. The compositor may decide to ignore the values set by the 1249 | * client and request a smaller size. 1250 | * 1251 | * If never set, or a value of zero in the request, means that the 1252 | * client has no expected minimum size in the given dimension. As a 1253 | * result, a client wishing to reset the minimum size to an 1254 | * unspecified state can use zero for width and height in the 1255 | * request. 1256 | * 1257 | * Requesting a minimum size to be larger than the maximum size of 1258 | * a surface is illegal and will result in a protocol error. 1259 | * 1260 | * The width and height must be greater than or equal to zero. 1261 | * Using strictly negative values for width and height will result 1262 | * in a protocol error. 1263 | */ 1264 | void (*set_min_size)(struct wl_client *client, 1265 | struct wl_resource *resource, 1266 | int32_t width, 1267 | int32_t height); 1268 | /** 1269 | * maximize the window 1270 | * 1271 | * Maximize the surface. 1272 | * 1273 | * After requesting that the surface should be maximized, the 1274 | * compositor will respond by emitting a configure event. Whether 1275 | * this configure actually sets the window maximized is subject to 1276 | * compositor policies. The client must then update its content, 1277 | * drawing in the configured state. The client must also 1278 | * acknowledge the configure when committing the new content (see 1279 | * ack_configure). 1280 | * 1281 | * It is up to the compositor to decide how and where to maximize 1282 | * the surface, for example which output and what region of the 1283 | * screen should be used. 1284 | * 1285 | * If the surface was already maximized, the compositor will still 1286 | * emit a configure event with the "maximized" state. 1287 | * 1288 | * If the surface is in a fullscreen state, this request has no 1289 | * direct effect. It may alter the state the surface is returned to 1290 | * when unmaximized unless overridden by the compositor. 1291 | */ 1292 | void (*set_maximized)(struct wl_client *client, 1293 | struct wl_resource *resource); 1294 | /** 1295 | * unmaximize the window 1296 | * 1297 | * Unmaximize the surface. 1298 | * 1299 | * After requesting that the surface should be unmaximized, the 1300 | * compositor will respond by emitting a configure event. Whether 1301 | * this actually un-maximizes the window is subject to compositor 1302 | * policies. If available and applicable, the compositor will 1303 | * include the window geometry dimensions the window had prior to 1304 | * being maximized in the configure event. The client must then 1305 | * update its content, drawing it in the configured state. The 1306 | * client must also acknowledge the configure when committing the 1307 | * new content (see ack_configure). 1308 | * 1309 | * It is up to the compositor to position the surface after it was 1310 | * unmaximized; usually the position the surface had before 1311 | * maximizing, if applicable. 1312 | * 1313 | * If the surface was already not maximized, the compositor will 1314 | * still emit a configure event without the "maximized" state. 1315 | * 1316 | * If the surface is in a fullscreen state, this request has no 1317 | * direct effect. It may alter the state the surface is returned to 1318 | * when unmaximized unless overridden by the compositor. 1319 | */ 1320 | void (*unset_maximized)(struct wl_client *client, 1321 | struct wl_resource *resource); 1322 | /** 1323 | * set the window as fullscreen on an output 1324 | * 1325 | * Make the surface fullscreen. 1326 | * 1327 | * After requesting that the surface should be fullscreened, the 1328 | * compositor will respond by emitting a configure event. Whether 1329 | * the client is actually put into a fullscreen state is subject to 1330 | * compositor policies. The client must also acknowledge the 1331 | * configure when committing the new content (see ack_configure). 1332 | * 1333 | * The output passed by the request indicates the client's 1334 | * preference as to which display it should be set fullscreen on. 1335 | * If this value is NULL, it's up to the compositor to choose which 1336 | * display will be used to map this surface. 1337 | * 1338 | * If the surface doesn't cover the whole output, the compositor 1339 | * will position the surface in the center of the output and 1340 | * compensate with with border fill covering the rest of the 1341 | * output. The content of the border fill is undefined, but should 1342 | * be assumed to be in some way that attempts to blend into the 1343 | * surrounding area (e.g. solid black). 1344 | * 1345 | * If the fullscreened surface is not opaque, the compositor must 1346 | * make sure that other screen content not part of the same surface 1347 | * tree (made up of subsurfaces, popups or similarly coupled 1348 | * surfaces) are not visible below the fullscreened surface. 1349 | */ 1350 | void (*set_fullscreen)(struct wl_client *client, 1351 | struct wl_resource *resource, 1352 | struct wl_resource *output); 1353 | /** 1354 | * unset the window as fullscreen 1355 | * 1356 | * Make the surface no longer fullscreen. 1357 | * 1358 | * After requesting that the surface should be unfullscreened, the 1359 | * compositor will respond by emitting a configure event. Whether 1360 | * this actually removes the fullscreen state of the client is 1361 | * subject to compositor policies. 1362 | * 1363 | * Making a surface unfullscreen sets states for the surface based 1364 | * on the following: * the state(s) it may have had before becoming 1365 | * fullscreen * any state(s) decided by the compositor * any 1366 | * state(s) requested by the client while the surface was 1367 | * fullscreen 1368 | * 1369 | * The compositor may include the previous window geometry 1370 | * dimensions in the configure event, if applicable. 1371 | * 1372 | * The client must also acknowledge the configure when committing 1373 | * the new content (see ack_configure). 1374 | */ 1375 | void (*unset_fullscreen)(struct wl_client *client, 1376 | struct wl_resource *resource); 1377 | /** 1378 | * set the window as minimized 1379 | * 1380 | * Request that the compositor minimize your surface. There is no 1381 | * way to know if the surface is currently minimized, nor is there 1382 | * any way to unset minimization on this surface. 1383 | * 1384 | * If you are looking to throttle redrawing when minimized, please 1385 | * instead use the wl_surface.frame event for this, as this will 1386 | * also work with live previews on windows in Alt-Tab, Expose or 1387 | * similar compositor features. 1388 | */ 1389 | void (*set_minimized)(struct wl_client *client, 1390 | struct wl_resource *resource); 1391 | }; 1392 | 1393 | #define XDG_TOPLEVEL_CONFIGURE 0 1394 | #define XDG_TOPLEVEL_CLOSE 1 1395 | 1396 | /** 1397 | * @ingroup iface_xdg_toplevel 1398 | */ 1399 | #define XDG_TOPLEVEL_CONFIGURE_SINCE_VERSION 1 1400 | /** 1401 | * @ingroup iface_xdg_toplevel 1402 | */ 1403 | #define XDG_TOPLEVEL_CLOSE_SINCE_VERSION 1 1404 | 1405 | /** 1406 | * @ingroup iface_xdg_toplevel 1407 | */ 1408 | #define XDG_TOPLEVEL_DESTROY_SINCE_VERSION 1 1409 | /** 1410 | * @ingroup iface_xdg_toplevel 1411 | */ 1412 | #define XDG_TOPLEVEL_SET_PARENT_SINCE_VERSION 1 1413 | /** 1414 | * @ingroup iface_xdg_toplevel 1415 | */ 1416 | #define XDG_TOPLEVEL_SET_TITLE_SINCE_VERSION 1 1417 | /** 1418 | * @ingroup iface_xdg_toplevel 1419 | */ 1420 | #define XDG_TOPLEVEL_SET_APP_ID_SINCE_VERSION 1 1421 | /** 1422 | * @ingroup iface_xdg_toplevel 1423 | */ 1424 | #define XDG_TOPLEVEL_SHOW_WINDOW_MENU_SINCE_VERSION 1 1425 | /** 1426 | * @ingroup iface_xdg_toplevel 1427 | */ 1428 | #define XDG_TOPLEVEL_MOVE_SINCE_VERSION 1 1429 | /** 1430 | * @ingroup iface_xdg_toplevel 1431 | */ 1432 | #define XDG_TOPLEVEL_RESIZE_SINCE_VERSION 1 1433 | /** 1434 | * @ingroup iface_xdg_toplevel 1435 | */ 1436 | #define XDG_TOPLEVEL_SET_MAX_SIZE_SINCE_VERSION 1 1437 | /** 1438 | * @ingroup iface_xdg_toplevel 1439 | */ 1440 | #define XDG_TOPLEVEL_SET_MIN_SIZE_SINCE_VERSION 1 1441 | /** 1442 | * @ingroup iface_xdg_toplevel 1443 | */ 1444 | #define XDG_TOPLEVEL_SET_MAXIMIZED_SINCE_VERSION 1 1445 | /** 1446 | * @ingroup iface_xdg_toplevel 1447 | */ 1448 | #define XDG_TOPLEVEL_UNSET_MAXIMIZED_SINCE_VERSION 1 1449 | /** 1450 | * @ingroup iface_xdg_toplevel 1451 | */ 1452 | #define XDG_TOPLEVEL_SET_FULLSCREEN_SINCE_VERSION 1 1453 | /** 1454 | * @ingroup iface_xdg_toplevel 1455 | */ 1456 | #define XDG_TOPLEVEL_UNSET_FULLSCREEN_SINCE_VERSION 1 1457 | /** 1458 | * @ingroup iface_xdg_toplevel 1459 | */ 1460 | #define XDG_TOPLEVEL_SET_MINIMIZED_SINCE_VERSION 1 1461 | 1462 | /** 1463 | * @ingroup iface_xdg_toplevel 1464 | * Sends an configure event to the client owning the resource. 1465 | * @param resource_ The client's resource 1466 | */ 1467 | static inline void 1468 | xdg_toplevel_send_configure(struct wl_resource *resource_, int32_t width, int32_t height, struct wl_array *states) 1469 | { 1470 | wl_resource_post_event(resource_, XDG_TOPLEVEL_CONFIGURE, width, height, states); 1471 | } 1472 | 1473 | /** 1474 | * @ingroup iface_xdg_toplevel 1475 | * Sends an close event to the client owning the resource. 1476 | * @param resource_ The client's resource 1477 | */ 1478 | static inline void 1479 | xdg_toplevel_send_close(struct wl_resource *resource_) 1480 | { 1481 | wl_resource_post_event(resource_, XDG_TOPLEVEL_CLOSE); 1482 | } 1483 | 1484 | #ifndef XDG_POPUP_ERROR_ENUM 1485 | #define XDG_POPUP_ERROR_ENUM 1486 | enum xdg_popup_error { 1487 | /** 1488 | * tried to grab after being mapped 1489 | */ 1490 | XDG_POPUP_ERROR_INVALID_GRAB = 0, 1491 | }; 1492 | #endif /* XDG_POPUP_ERROR_ENUM */ 1493 | 1494 | /** 1495 | * @ingroup iface_xdg_popup 1496 | * @struct xdg_popup_interface 1497 | */ 1498 | struct xdg_popup_interface { 1499 | /** 1500 | * remove xdg_popup interface 1501 | * 1502 | * This destroys the popup. Explicitly destroying the xdg_popup 1503 | * object will also dismiss the popup, and unmap the surface. 1504 | * 1505 | * If this xdg_popup is not the "topmost" popup, a protocol error 1506 | * will be sent. 1507 | */ 1508 | void (*destroy)(struct wl_client *client, 1509 | struct wl_resource *resource); 1510 | /** 1511 | * make the popup take an explicit grab 1512 | * 1513 | * This request makes the created popup take an explicit grab. An 1514 | * explicit grab will be dismissed when the user dismisses the 1515 | * popup, or when the client destroys the xdg_popup. This can be 1516 | * done by the user clicking outside the surface, using the 1517 | * keyboard, or even locking the screen through closing the lid or 1518 | * a timeout. 1519 | * 1520 | * If the compositor denies the grab, the popup will be immediately 1521 | * dismissed. 1522 | * 1523 | * This request must be used in response to some sort of user 1524 | * action like a button press, key press, or touch down event. The 1525 | * serial number of the event should be passed as 'serial'. 1526 | * 1527 | * The parent of a grabbing popup must either be an xdg_toplevel 1528 | * surface or another xdg_popup with an explicit grab. If the 1529 | * parent is another xdg_popup it means that the popups are nested, 1530 | * with this popup now being the topmost popup. 1531 | * 1532 | * Nested popups must be destroyed in the reverse order they were 1533 | * created in, e.g. the only popup you are allowed to destroy at 1534 | * all times is the topmost one. 1535 | * 1536 | * When compositors choose to dismiss a popup, they may dismiss 1537 | * every nested grabbing popup as well. When a compositor dismisses 1538 | * popups, it will follow the same dismissing order as required 1539 | * from the client. 1540 | * 1541 | * The parent of a grabbing popup must either be another xdg_popup 1542 | * with an active explicit grab, or an xdg_popup or xdg_toplevel, 1543 | * if there are no explicit grabs already taken. 1544 | * 1545 | * If the topmost grabbing popup is destroyed, the grab will be 1546 | * returned to the parent of the popup, if that parent previously 1547 | * had an explicit grab. 1548 | * 1549 | * If the parent is a grabbing popup which has already been 1550 | * dismissed, this popup will be immediately dismissed. If the 1551 | * parent is a popup that did not take an explicit grab, an error 1552 | * will be raised. 1553 | * 1554 | * During a popup grab, the client owning the grab will receive 1555 | * pointer and touch events for all their surfaces as normal 1556 | * (similar to an "owner-events" grab in X11 parlance), while the 1557 | * top most grabbing popup will always have keyboard focus. 1558 | * @param seat the wl_seat of the user event 1559 | * @param serial the serial of the user event 1560 | */ 1561 | void (*grab)(struct wl_client *client, 1562 | struct wl_resource *resource, 1563 | struct wl_resource *seat, 1564 | uint32_t serial); 1565 | /** 1566 | * recalculate the popup's location 1567 | * 1568 | * Reposition an already-mapped popup. The popup will be placed 1569 | * given the details in the passed xdg_positioner object, and a 1570 | * xdg_popup.repositioned followed by xdg_popup.configure and 1571 | * xdg_surface.configure will be emitted in response. Any 1572 | * parameters set by the previous positioner will be discarded. 1573 | * 1574 | * The passed token will be sent in the corresponding 1575 | * xdg_popup.repositioned event. The new popup position will not 1576 | * take effect until the corresponding configure event is 1577 | * acknowledged by the client. See xdg_popup.repositioned for 1578 | * details. The token itself is opaque, and has no other special 1579 | * meaning. 1580 | * 1581 | * If multiple reposition requests are sent, the compositor may 1582 | * skip all but the last one. 1583 | * 1584 | * If the popup is repositioned in response to a configure event 1585 | * for its parent, the client should send an 1586 | * xdg_positioner.set_parent_configure and possibly an 1587 | * xdg_positioner.set_parent_size request to allow the compositor 1588 | * to properly constrain the popup. 1589 | * 1590 | * If the popup is repositioned together with a parent that is 1591 | * being resized, but not in response to a configure event, the 1592 | * client should send an xdg_positioner.set_parent_size request. 1593 | * @param token reposition request token 1594 | * @since 3 1595 | */ 1596 | void (*reposition)(struct wl_client *client, 1597 | struct wl_resource *resource, 1598 | struct wl_resource *positioner, 1599 | uint32_t token); 1600 | }; 1601 | 1602 | #define XDG_POPUP_CONFIGURE 0 1603 | #define XDG_POPUP_POPUP_DONE 1 1604 | #define XDG_POPUP_REPOSITIONED 2 1605 | 1606 | /** 1607 | * @ingroup iface_xdg_popup 1608 | */ 1609 | #define XDG_POPUP_CONFIGURE_SINCE_VERSION 1 1610 | /** 1611 | * @ingroup iface_xdg_popup 1612 | */ 1613 | #define XDG_POPUP_POPUP_DONE_SINCE_VERSION 1 1614 | /** 1615 | * @ingroup iface_xdg_popup 1616 | */ 1617 | #define XDG_POPUP_REPOSITIONED_SINCE_VERSION 3 1618 | 1619 | /** 1620 | * @ingroup iface_xdg_popup 1621 | */ 1622 | #define XDG_POPUP_DESTROY_SINCE_VERSION 1 1623 | /** 1624 | * @ingroup iface_xdg_popup 1625 | */ 1626 | #define XDG_POPUP_GRAB_SINCE_VERSION 1 1627 | /** 1628 | * @ingroup iface_xdg_popup 1629 | */ 1630 | #define XDG_POPUP_REPOSITION_SINCE_VERSION 3 1631 | 1632 | /** 1633 | * @ingroup iface_xdg_popup 1634 | * Sends an configure event to the client owning the resource. 1635 | * @param resource_ The client's resource 1636 | * @param x x position relative to parent surface window geometry 1637 | * @param y y position relative to parent surface window geometry 1638 | * @param width window geometry width 1639 | * @param height window geometry height 1640 | */ 1641 | static inline void 1642 | xdg_popup_send_configure(struct wl_resource *resource_, int32_t x, int32_t y, int32_t width, int32_t height) 1643 | { 1644 | wl_resource_post_event(resource_, XDG_POPUP_CONFIGURE, x, y, width, height); 1645 | } 1646 | 1647 | /** 1648 | * @ingroup iface_xdg_popup 1649 | * Sends an popup_done event to the client owning the resource. 1650 | * @param resource_ The client's resource 1651 | */ 1652 | static inline void 1653 | xdg_popup_send_popup_done(struct wl_resource *resource_) 1654 | { 1655 | wl_resource_post_event(resource_, XDG_POPUP_POPUP_DONE); 1656 | } 1657 | 1658 | /** 1659 | * @ingroup iface_xdg_popup 1660 | * Sends an repositioned event to the client owning the resource. 1661 | * @param resource_ The client's resource 1662 | * @param token reposition request token 1663 | */ 1664 | static inline void 1665 | xdg_popup_send_repositioned(struct wl_resource *resource_, uint32_t token) 1666 | { 1667 | wl_resource_post_event(resource_, XDG_POPUP_REPOSITIONED, token); 1668 | } 1669 | 1670 | #ifdef __cplusplus 1671 | } 1672 | #endif 1673 | 1674 | #endif 1675 | --------------------------------------------------------------------------------