mowc

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/mowc
Log | Files | Refs

commit bc38196909d7b6186fffac43012b7f470104ec6c
parent 785a266454e6431df5de95985b436000a688d063
Author: Nihal Jere <nihal@nihaljere.xyz>
Date:   Sun, 13 Jun 2021 17:39:20 -0500

working call reception

Diffstat:
Aclients/answer.c | 300+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aclients/call.c | 330+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mclients/calld.c | 192+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Aclients/config.h | 4++++
Adrw.c | 407+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrw.h | 48++++++++++++++++++++++++++++++++++++++++++++++++
Autil.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Autil.h | 10++++++++++
8 files changed, 1302 insertions(+), 52 deletions(-)

diff --git a/clients/answer.c b/clients/answer.c @@ -0,0 +1,300 @@ +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wld/wayland.h> +#include <wld/wld.h> +#include <wayland-client.h> +#include <xdg-shell-client-protocol.h> + +#include "drw.h" +#include "util.h" + +#define LENGTH(x) (sizeof x / sizeof x[0]) +#define STRINGTOKEYSYM(X) (XStringToxkb_keysym_t(X)) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X))) + +enum { + SchemeNormal, + SchemeAccept, + SchemeDecline, + SchemeAcceptDown, + SchemeDeclineDown, + SchemeLast +}; + +enum button { + ButtonNone, + ButtonAccept, + ButtonDecline, +}; + +#include "config.h" + +static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNormal] = { "#ffffff", "#2222cc" }, + [SchemeAccept] = { "#ffffff", "#22cc22" }, + [SchemeDecline] = { "#ffffff", "#cc2222" }, + [SchemeAcceptDown] = { "#ffffff", "#008800" }, + [SchemeDeclineDown] = { "#ffffff", "#880000" }, +}; + +static Clr* scheme[SchemeLast]; + +struct { + struct wl_display *dpy; + struct wl_compositor *cmp; + struct wl_seat *seat; + struct wl_touch *touch; + struct wl_surface *surface; + struct xdg_wm_base *wm; + struct xdg_surface *xdgsurface; + struct xdg_toplevel *toplevel; + Drw *drw; + + int32_t w, h; +} wl; + +char *name; +unsigned int tw, th; +enum button pressed, current; + +void wmping(void *data, struct xdg_wm_base *wm, uint32_t serial); +void xdgsurfconfigure(void *data, struct xdg_surface *surf, uint32_t serial); +void toplevelconfigure(void *data, struct xdg_toplevel *toplevel, int32_t w, int32_t h, struct wl_array *states); +void toplevelclose(void *data, struct xdg_toplevel *toplevel); +void regglobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version); +void regglobalremove(void *data, struct wl_registry *registry, uint32_t name); +void draw(void); +void touchdown(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, struct wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y); +void touchup(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, int32_t id); +void touchmotion(void *data, struct wl_touch *wl_touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y); +void touchframe(void *data, struct wl_touch *wl_touch); +void touchcancel(void *data, struct wl_touch *wl_touch); + +static struct wl_registry_listener reglistener = { regglobal, regglobalremove }; + +static struct xdg_wm_base_listener wmlistener = { wmping }; +static struct xdg_surface_listener xdgsurflistener = { xdgsurfconfigure }; +static struct xdg_toplevel_listener toplevellistener = + { toplevelconfigure, toplevelclose }; +static struct wl_touch_listener touchlistener = + { touchdown, touchup, touchmotion, touchframe, touchcancel }; + +void +wmping(void *data, struct xdg_wm_base *wm, uint32_t serial) +{ + xdg_wm_base_pong(wm, serial); +} + +void +xdgsurfconfigure(void *data, struct xdg_surface *surf, uint32_t serial) +{ + xdg_surface_ack_configure(surf, serial); +} + +void +toplevelconfigure(void *data, struct xdg_toplevel *toplevel, int32_t w, int32_t h, + struct wl_array *states) +{ + if (w == wl.w && h == wl.h) + return; + if (w == 0 && h == 0) + return; + + wl.w = w; + wl.h = h; +} + +void +toplevelclose(void *data, struct xdg_toplevel *toplevel) +{ + exit(0); +} + +void +regglobal(void *data, struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) +{ + if (strcmp(interface, "wl_compositor") == 0) { + wl.cmp = wl_registry_bind(registry, name, + &wl_compositor_interface, 3); + } else if (strcmp(interface, "xdg_wm_base") == 0) { + wl.wm = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(wl.wm, &wmlistener, NULL); + } else if (strcmp(interface, "wl_seat") == 0) { + wl.seat = wl_registry_bind(registry, name, + &wl_seat_interface, 4); + } +} + +void +regglobalremove(void *data, struct wl_registry *registry, uint32_t name) +{ +} + +struct { + bool down; + uint32_t x, y; + int32_t id; +} touchpoint; + +static enum button +getpressed() +{ + if (touchpoint.x < wl.w / 2 && touchpoint.y > 7 * wl.h / 8) { + return ButtonAccept; + } else if (touchpoint.x >= wl.w / 2 && touchpoint.y > 7 * wl.h / 8) { + return ButtonDecline; + } + return ButtonNone; +} + +void +touchdown(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, struct wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y) +{ + if (touchpoint.down) + return; + + touchpoint.down = true; + touchpoint.x = wl_fixed_to_int(x); + touchpoint.y = wl_fixed_to_int(y); + touchpoint.id = id; + current = pressed = getpressed(); + draw(); +} + +void +touchup(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, int32_t id) +{ + if (touchpoint.id != id) + return; + + touchpoint.down = false; + + if (pressed == current) { + if (pressed == ButtonDecline) { + exit(1); + } else if (pressed == ButtonAccept) { + exit(0); + } + } + + current = pressed = ButtonNone; + draw(); +} + +void +touchmotion(void *data, struct wl_touch *wl_touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) +{ + if (touchpoint.id != id) + return; + + touchpoint.x = wl_fixed_to_int(x); + touchpoint.y = wl_fixed_to_int(y); + current = getpressed(); + draw(); +} + +void +touchframe(void *data, struct wl_touch *wl_touch) +{ +} + +void touchcancel(void *data, struct wl_touch *wl_touch) +{ + touchpoint.down = false; +} + +void +draw() +{ + int height = wl.h / 8; + wld_set_target_surface(wl.drw->renderer, wl.drw->surface); + + drw_setscheme(wl.drw, scheme[SchemeNormal]); + drw_rect(wl.drw, 0, 0, wl.w, wl.h - height, 1, 1); + drw_setfontset(wl.drw, &wl.drw->fonts[0]); + drw_text(wl.drw, wl.w / 2 - tw / 2, height, tw, th, 0, name, 0); + + drw_setscheme(wl.drw, pressed == ButtonAccept ? scheme[SchemeAcceptDown] : scheme[SchemeAccept]); + drw_rect(wl.drw, 0, wl.h - height, wl.w / 2, height, 1, 1); + + drw_setscheme(wl.drw, pressed == ButtonDecline ? scheme[SchemeDeclineDown] : scheme[SchemeDecline]); + drw_rect(wl.drw, wl.w / 2, wl.h - height, wl.w / 2, height, 1, 1); + + drw_map(wl.drw, wl.surface, 0, 0, wl.w, wl.h); +} + +void +wlinit(void) +{ + struct wl_registry *registry; + int j; + + wl.w = wl.h = 500; + + if (!(wl.dpy = wl_display_connect(NULL))) + die("Can't open display\n"); + + registry = wl_display_get_registry(wl.dpy); + wl_registry_add_listener(registry, &reglistener, NULL); + + wl_display_roundtrip(wl.dpy); + + wl.drw = drw_create(wl.dpy); + + if (!wl.seat) + die("Display has no seat\n"); + if (!wl.wm) + die("Display has no window manager\n"); + + if (!drw_fontset_create(wl.drw, (const char **) fonts, LENGTH(fonts))) + die("no fonts could be loaded"); + + wl.touch = wl_seat_get_touch(wl.seat); + wl_touch_add_listener(wl.touch, &touchlistener, NULL); + + wl_display_roundtrip(wl.dpy); + + if (!wl.touch) + die("seat has no touch support\n"); + + drw_font_getexts(&wl.drw->fonts[0], name, strlen(name), &tw, &th); + + /* init appearance */ + for (j = 0; j < SchemeLast; j++) + scheme[j] = drw_scm_create(wl.drw, (const char **) colors[j], 2); + + wl.surface = wl_compositor_create_surface(wl.cmp); + wl.xdgsurface = xdg_wm_base_get_xdg_surface(wl.wm, wl.surface); + xdg_surface_add_listener(wl.xdgsurface, &xdgsurflistener, NULL); + wl.toplevel = xdg_surface_get_toplevel(wl.xdgsurface); + xdg_toplevel_add_listener(wl.toplevel, &toplevellistener, NULL); + xdg_toplevel_set_app_id(wl.toplevel, "answer"); + + wl_display_roundtrip(wl.dpy); + + if (wl.w == 0 || wl.h == 0) { + die("window has 0 dimension\n"); + } + + drw_resize(wl.drw, wl.surface, wl.w, wl.h); + draw(); +} + +int +main(int argc, char *argv[]) +{ + if (argc != 2) { + die("need exactly 1 argument\n"); + } + + name = argv[1]; + + wlinit(); + while (wl_display_dispatch(wl.dpy) != -1); + return 0; +} diff --git a/clients/call.c b/clients/call.c @@ -0,0 +1,330 @@ +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wld/wayland.h> +#include <wld/wld.h> +#include <wayland-client.h> +#include <xdg-shell-client-protocol.h> + +#include "drw.h" +#include "util.h" + +#define LENGTH(x) (sizeof x / sizeof x[0]) +#define STRINGTOKEYSYM(X) (XStringToxkb_keysym_t(X)) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X))) + +enum { + SchemeNormal, + SchemeSetting, + SchemeSettingDown, + SchemeHangup, + SchemeHangupDown, + SchemeLast +}; + +enum button { + ButtonNone, + ButtonInput, + ButtonOutput, + ButtonHangup, +}; + +enum inputstate { + InputStateUnmuted, + InputStateMuted, + InputStateLast, +}; + +enum outputstate { + OutputStateSpeaker, + OutputStateAudioJack, + OutputStateEarpiece, + OutputStateLast, +}; + +static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNormal] = { "#ffffff", "#2222cc" }, + [SchemeSetting] = { "#ffffff", "#22cc22" }, + [SchemeHangup] = { "#ffffff", "#cc2222" }, + [SchemeSettingDown] = { "#ffffff", "#008800" }, + [SchemeHangupDown] = { "#ffffff", "#880000" }, +}; + +#include "config.h" + +static Clr* scheme[SchemeLast]; + +struct { + struct wl_display *dpy; + struct wl_compositor *cmp; + struct wl_seat *seat; + struct wl_touch *touch; + struct wl_surface *surface; + struct xdg_wm_base *wm; + struct xdg_surface *xdgsurface; + struct xdg_toplevel *toplevel; + Drw *drw; + + int32_t w, h; +} wl; + +char *name; +unsigned int tw, th; +enum button pressed, current; + +void wmping(void *data, struct xdg_wm_base *wm, uint32_t serial); +void xdgsurfconfigure(void *data, struct xdg_surface *surf, uint32_t serial); +void toplevelconfigure(void *data, struct xdg_toplevel *toplevel, int32_t w, int32_t h, struct wl_array *states); +void toplevelclose(void *data, struct xdg_toplevel *toplevel); +void regglobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version); +void regglobalremove(void *data, struct wl_registry *registry, uint32_t name); +void draw(void); +void touchdown(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, struct wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y); +void touchup(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, int32_t id); +void touchmotion(void *data, struct wl_touch *wl_touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y); +void touchframe(void *data, struct wl_touch *wl_touch); +void touchcancel(void *data, struct wl_touch *wl_touch); + +static struct wl_registry_listener reglistener = { regglobal, regglobalremove }; + +static struct xdg_wm_base_listener wmlistener = { wmping }; +static struct xdg_surface_listener xdgsurflistener = { xdgsurfconfigure }; +static struct xdg_toplevel_listener toplevellistener = + { toplevelconfigure, toplevelclose }; +static struct wl_touch_listener touchlistener = + { touchdown, touchup, touchmotion, touchframe, touchcancel }; + +void +wmping(void *data, struct xdg_wm_base *wm, uint32_t serial) +{ + xdg_wm_base_pong(wm, serial); +} + +void +xdgsurfconfigure(void *data, struct xdg_surface *surf, uint32_t serial) +{ + xdg_surface_ack_configure(surf, serial); +} + +void +toplevelconfigure(void *data, struct xdg_toplevel *toplevel, int32_t w, int32_t h, + struct wl_array *states) +{ + if (w == wl.w && h == wl.h) + return; + if (w == 0 && h == 0) + return; + + wl.w = w; + wl.h = h; +} + +void +toplevelclose(void *data, struct xdg_toplevel *toplevel) +{ + exit(0); +} + +void +regglobal(void *data, struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) +{ + if (strcmp(interface, "wl_compositor") == 0) { + wl.cmp = wl_registry_bind(registry, name, + &wl_compositor_interface, 3); + } else if (strcmp(interface, "xdg_wm_base") == 0) { + wl.wm = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(wl.wm, &wmlistener, NULL); + } else if (strcmp(interface, "wl_seat") == 0) { + wl.seat = wl_registry_bind(registry, name, + &wl_seat_interface, 4); + } +} + +void +regglobalremove(void *data, struct wl_registry *registry, uint32_t name) +{ +} + +struct { + bool down; + uint32_t x, y; + int32_t id; +} touchpoint; + +static enum button +getpressed() +{ + if (touchpoint.y > 7 * wl.h / 8) { + return ButtonHangup; + } else if (touchpoint.x < wl.w / 2 && touchpoint.y > 3 * wl.h / 8 && touchpoint.y < wl.h / 2) { + return ButtonInput; + } else if (touchpoint.x > wl.w / 2 && touchpoint.y > 3 * wl.h / 8 && touchpoint.y < wl.h / 2) { + return ButtonOutput; + } + + return ButtonNone; +} + +void +touchdown(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, struct wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y) +{ + if (touchpoint.down) + return; + + touchpoint.down = true; + touchpoint.x = wl_fixed_to_int(x); + touchpoint.y = wl_fixed_to_int(y); + touchpoint.id = id; + current = pressed = getpressed(); + draw(); +} + +int switchinput() { +} + +void +touchup(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, int32_t id) +{ + if (touchpoint.id != id) + return; + + touchpoint.down = false; + + if (pressed == current) { + switch (pressed) { + case ButtonInput: + fprintf(stderr, "ButtonInput\n"); + break; + case ButtonOutput: + fprintf(stderr, "ButtonOutput\n"); + break; + case ButtonHangup: + fprintf(stderr, "ButtonHangup\n"); + exit(0); + break; + } + } + + current = pressed = ButtonNone; + draw(); +} + +void +touchmotion(void *data, struct wl_touch *wl_touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) +{ + if (touchpoint.id != id) + return; + + touchpoint.x = wl_fixed_to_int(x); + touchpoint.y = wl_fixed_to_int(y); + current = getpressed(); + draw(); +} + +void +touchframe(void *data, struct wl_touch *wl_touch) +{ +} + +void touchcancel(void *data, struct wl_touch *wl_touch) +{ + touchpoint.down = false; +} + +void +draw() +{ + int height = wl.h / 8; + wld_set_target_surface(wl.drw->renderer, wl.drw->surface); + + drw_setscheme(wl.drw, scheme[SchemeNormal]); + drw_rect(wl.drw, 0, 0, wl.w, wl.h - height, 1, 1); + drw_setfontset(wl.drw, &wl.drw->fonts[0]); + drw_text(wl.drw, wl.w / 2 - tw / 2, height, tw, th, 0, name, 0); + + drw_setscheme(wl.drw, pressed == ButtonInput ? scheme[SchemeSetting] : scheme[SchemeSettingDown]); + drw_rect(wl.drw, 0, 3 * wl.h / 8, wl.w / 2, height, 1, 1); + + drw_setscheme(wl.drw, pressed == ButtonOutput ? scheme[SchemeSetting] : scheme[SchemeSettingDown]); + drw_rect(wl.drw, wl.w / 2, 3 * wl.h / 8, wl.w / 2, height, 1, 1); + + drw_setscheme(wl.drw, pressed == ButtonHangup ? scheme[SchemeHangup] : scheme[SchemeHangupDown]); + drw_rect(wl.drw, 0, 7 * wl.h / 8, wl.w, height, 1, 1); + + drw_map(wl.drw, wl.surface, 0, 0, wl.w, wl.h); +} + +void +wlinit(void) +{ + struct wl_registry *registry; + int j; + + wl.w = wl.h = 500; + + if (!(wl.dpy = wl_display_connect(NULL))) + die("Can't open display\n"); + + registry = wl_display_get_registry(wl.dpy); + wl_registry_add_listener(registry, &reglistener, NULL); + + wl_display_roundtrip(wl.dpy); + + wl.drw = drw_create(wl.dpy); + + if (!wl.seat) + die("Display has no seat\n"); + if (!wl.wm) + die("Display has no window manager\n"); + + if (!drw_fontset_create(wl.drw, (const char **) fonts, LENGTH(fonts))) + die("no fonts could be loaded"); + + wl.touch = wl_seat_get_touch(wl.seat); + wl_touch_add_listener(wl.touch, &touchlistener, NULL); + + wl_display_roundtrip(wl.dpy); + + if (!wl.touch) + die("seat has no touch support\n"); + + drw_font_getexts(&wl.drw->fonts[0], name, strlen(name), &tw, &th); + + /* init appearance */ + for (j = 0; j < SchemeLast; j++) + scheme[j] = drw_scm_create(wl.drw, (const char **) colors[j], 2); + + wl.surface = wl_compositor_create_surface(wl.cmp); + wl.xdgsurface = xdg_wm_base_get_xdg_surface(wl.wm, wl.surface); + xdg_surface_add_listener(wl.xdgsurface, &xdgsurflistener, NULL); + wl.toplevel = xdg_surface_get_toplevel(wl.xdgsurface); + xdg_toplevel_add_listener(wl.toplevel, &toplevellistener, NULL); + xdg_toplevel_set_app_id(wl.toplevel, "answer"); + + wl_display_roundtrip(wl.dpy); + + if (wl.w == 0 || wl.h == 0) { + die("window has 0 dimension\n"); + } + + drw_resize(wl.drw, wl.surface, wl.w, wl.h); + draw(); +} + +int +main(int argc, char *argv[]) +{ + if (argc != 2) { + die("need exactly 1 argument\n"); + } + + name = argv[1]; + + wlinit(); + while (wl_display_dispatch(wl.dpy) != -1); + return 0; +} diff --git a/clients/calld.c b/clients/calld.c @@ -1,78 +1,110 @@ #include <assert.h> #include <poll.h> +#include <signal.h> #include <stdio.h> #include <string.h> +#include <sys/signalfd.h> #include <sys/socket.h> #include <sys/un.h> #include <unistd.h> #include <atd.h> #include <encdec.h> +#include "util.h" #ifndef ATD_PATH #define ATD_PATH "/tmp/atd-socket" #endif +#define CALL_PROGRAM MOWC_LIBEXEC "/call" +#define ANSWER_PROGRAM MOWC_LIBEXEC "/answer" + #define ATD 0 +#define SIG 1 #define NUMFD 2 -struct call calls[MAX_CALLS]; -struct call calls2[MAX_CALLS]; -struct call *active; +struct call deccall; +struct call current = { .status = CALL_INACTIVE }; +pid_t childpid; + +enum { + STATE_INACTIVE, + STATE_INCALL, + STATE_ANSWER, +} state; + +int +printstate() +{ + switch (state) { + case STATE_INACTIVE: fprintf(stderr, "state: inactive\n"); + case STATE_INCALL: fprintf(stderr, "state: incall\n"); + case STATE_ANSWER: fprintf(stderr, "state: answer\n"); + } +} + +int +printcall() +{ + fprintf(stderr, "call: %s, %d\n", current.num, current.status); +} + +int +launchchild(char *prog, char *number) +{ + pid_t pid = fork(); + if (pid < 0) { + return -1; + } else if (pid == 0) { + execl(prog, prog, number, NULL); + } else { + childpid = pid; + } + return 0; +} void handle_update() { - for (int i = 0; i < MAX_CALLS; i++) { - /* there is no active call with the id */ - if (!(calls[i].present || calls2[i].present)) - continue; - - /* an active call has been ended */ - if (calls[i].present && !calls2[i].present && active == &calls[i]) { - active = NULL; - fprintf(stderr, "kill\n"); - /* kill call program */ - continue; + fprintf(stderr, "handle update\n"); + // TODO handle multiple calls + switch (current.status) { + case CALL_INACTIVE: + if (deccall.status == CALL_INCOMING) { + current.status = deccall.status; + strcpy(current.num, deccall.num); + state = STATE_ANSWER; + launchchild(ANSWER_PROGRAM, current.num); } - - /* a new call */ - if (!calls[i].present) { - if (calls2[i].status == CALL_DIALING) { - fprintf(stderr, "launch call\n"); - /* launch call program */ - } else if (calls2[i].status == CALL_INCOMING) { - /* launch call answer program */ - fprintf(stderr, "launch answer\n"); - } - continue; + break; + + case CALL_DIALING: + if (deccall.status == CALL_ANSWERED && strcmp(current.num, deccall.num) == 0) { + current.status = CALL_ACTIVE; + state = STATE_INCALL; + launchchild(CALL_PROGRAM, current.num); + } else if (deccall.status == CALL_INACTIVE) { + state = STATE_INACTIVE; + current.status = deccall.status; + memset(current.num, 0, PHONE_NUMBER_MAX_LEN + 1); + assert(childpid > 0); + kill(childpid, SIGTERM); + childpid = 0; } - - /* TODO handle all cases */ - switch (calls[i].status) { - case CALL_DIALING: - if (calls2[i].status == CALL_ACTIVE) { - assert(!active); - active = &calls[i]; - /* launch call program */ - fprintf(stderr, "launch call\n"); - } - break; - case CALL_INCOMING: - if (calls2[i].status == CALL_ACTIVE) { - assert(!active); - active = &calls[i]; - /* launch call program */ - fprintf(stderr, "launch call\n"); - } - break; - default: - fprintf(stderr, "transition from %d to %d unhandled\n", calls[i].status, calls2[i].status); + break; + + case CALL_ACTIVE: + if (deccall.status == CALL_INACTIVE) { + current.status = deccall.status; + memset(current.num, 0, PHONE_NUMBER_MAX_LEN + 1); + assert(childpid > 0); + kill(childpid, SIGTERM); + state = STATE_INACTIVE; + childpid = 0; } } - - memcpy(calls, calls2, sizeof(calls)); - memset(calls2, 0, sizeof(calls2)); + printcall(); + printstate(); } int @@ -83,6 +115,17 @@ main(int argc, char *argv[]) .sun_path = ATD_PATH, }; + struct signalfd_siginfo siginfo; + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + + if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) { + fprintf(stderr, "failed to set block signal\n"); + return 1; + } + int sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock == -1) { fprintf(stderr, "failed to open atd socket\n"); @@ -107,7 +150,9 @@ main(int argc, char *argv[]) fds[ATD].fd = sock; fds[ATD].events = POLLIN; - fds[1].fd = -1; + fds[SIG].fd = signalfd(-1, &mask, 0); + fds[SIG].events = POLLIN; + fds[2].fd = -1; while (true) { if (poll(fds, NUMFD, -1) == -1) break; @@ -120,13 +165,56 @@ main(int argc, char *argv[]) } if (status == STATUS_CALL) { - if (dec_call_status(fds[ATD].fd, calls2) < 0) { + if (dec_call_status(fds[ATD].fd, &deccall) < 0) { fprintf(stderr, "bad call status from atd\n"); break; } + + handle_update(); } + } - handle_update(); + if (fds[SIG].revents & POLLIN) { + fprintf(stderr, "received signal!\n"); + printstate(); + ret = read(fds[SIG].fd, &siginfo, sizeof(siginfo)); + /* call/answer has terminated */ + if (siginfo.ssi_signo == SIGCHLD) { + switch (state) { + case STATE_INCALL: + fprintf(stderr, "incall: %u\n", siginfo.ssi_status); + state = STATE_INACTIVE; + memset(current.num, 0, PHONE_NUMBER_MAX_LEN + 1); + current.status = CALL_INACTIVE; + childpid = 0; + /* if the call program is terminated but we didn't kill it, + * then hangup up the call */ + if (siginfo.ssi_status != 128 + SIGTERM) { + atd_cmd_hangup(sock); + } + break; + case STATE_ANSWER: + childpid = 0; + /* only accept call if answer program returns 0, + * otherwise terminate */ + if (siginfo.ssi_status == 0) { + fprintf(stderr, "call accepted!\n"); + atd_cmd_answer(sock); + state = STATE_INCALL; + launchchild(CALL_PROGRAM, current.num); + } else { + fprintf(stderr, "call declined!\n"); + atd_cmd_hangup(sock); + state = STATE_INACTIVE; + memset(current.num, 0, PHONE_NUMBER_MAX_LEN + 1); + current.status = CALL_INACTIVE; + } + break; + default: + assert("not possible!\n"); + } + } + childpid = 0; } } err: diff --git a/clients/config.h b/clients/config.h @@ -0,0 +1,4 @@ +static const char *fonts[] = { + "DejaVu Sans:bold:size=50" +}; + diff --git a/drw.c b/drw.c @@ -0,0 +1,407 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wayland-client.h> +#include <wld/wld.h> +#include <wld/wayland.h> + +#include "drw.h" +#include "util.h" + +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 + +static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +static long +utf8decodebyte(const char c, size_t *i) +{ + for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) + if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) + return (unsigned char)c & ~utfmask[*i]; + return 0; +} + +static size_t +utf8validate(long *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + return i; +} + +static size_t +utf8decode(const char *c, long *u, size_t clen) +{ + size_t i, j, len, type; + long udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Drw * +drw_create(struct wl_display *dpy) +{ + Drw *drw = ecalloc(1, sizeof(Drw)); + + drw->dpy = dpy; + drw->ctx = wld_wayland_create_context(dpy, WLD_SHM); + drw->renderer = wld_create_renderer(drw->ctx); + drw->fontctx = wld_font_create_context(); + + return drw; +} + +void +drw_resize(Drw *drw, struct wl_surface *surface, unsigned int w, unsigned int h) +{ + if (drw->surface) + wld_destroy_surface(drw->surface); + drw->surface = wld_wayland_create_surface(drw->ctx, w, h, WLD_FORMAT_XRGB8888, 0, surface); +} + +void +drw_free(Drw *drw) +{ + wld_destroy_surface(drw->surface); + wld_destroy_renderer(drw->renderer); + wld_destroy_context(drw->ctx); + drw_fontset_free(drw->fonts); + wld_font_destroy_context(drw->fontctx); + free(drw); +} + +/* This function is an implementation detail. Library users should use + * drw_fontset_create instead. + */ +static Fnt * +wldfont_create(Drw *drw, const char *fontname, FcPattern *pattern) +{ + Fnt *font; + struct wld_font *wld = NULL; + + if (fontname) { + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by + * FcNameParse; using the latter results in the desired fallback + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ + if (!(wld = wld_font_open_name(drw->fontctx, fontname))) { + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + wld_font_close(wld); + return NULL; + } + } else if (pattern) { + if (!(wld = wld_font_open_pattern(drw->fontctx, pattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; + } + } else { + die("no font specified."); + } + + /* Do not allow using color fonts. This is a workaround for a BadLength + * error from Xft with color glyphs. Modelled on the Xterm workaround. See + * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 + * https://lists.suckless.org/dev/1701/30932.html + * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 + * and lots more all over the internet. + */ + FcBool iscol; + if(FcPatternGetBool(pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { + wld_font_close(wld); + return NULL; + } + + font = ecalloc(1, sizeof(Fnt)); + font->wld = wld; + font->pattern = pattern; + + return font; +} + +static void +wldfont_free(Fnt *font) +{ + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + wld_font_close(font->wld); + free(font); +} + +Fnt* +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) +{ + Fnt *cur, *ret = NULL; + size_t i; + + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = wldfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; + } + } + return (drw->fonts = ret); +} + +void +drw_fontset_free(Fnt *font) +{ + if (font) { + drw_fontset_free(font->next); + wldfont_free(font); + } +} + +void +drw_clr_create(Drw *drw, Clr *dest, const char *clrname) +{ + if (!drw || !dest || !clrname) + return; + + if (!(wld_lookup_named_color(clrname, dest))) + die("error, cannot allocate color '%s'", clrname); +} + +/* Wrapper to create color schemes. The caller has to call free(3) on the + * returned color scheme when done using it. */ +Clr * +drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) +{ + size_t i; + Clr *ret; + + /* need at least two colors for a scheme */ + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(*ret)))) + die("error, cannot create color scheme (drw=%d) (clrcount=%d)", drw, clrcount); + + for (i = 0; i < clrcount; i++) + drw_clr_create(drw, &ret[i], clrnames[i]); + return ret; +} + +void +drw_setfontset(Drw *drw, Fnt *set) +{ + if (drw) + drw->fonts = set; +} + +void +drw_setscheme(Drw *drw, Clr *scm) +{ + if (drw) + drw->scheme = scm; +} + +void +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) +{ + if (!drw || !drw->scheme) + return; + Clr color = invert ? drw->scheme[ColBg] : drw->scheme[ColFg]; + if (filled) + wld_fill_rectangle(drw->renderer, color, x, y, w, h); + else { + wld_fill_rectangle(drw->renderer, color, x, y, w, 1); + wld_fill_rectangle(drw->renderer, color, x + w - 1, y + 1, 1, h - 2); + wld_fill_rectangle(drw->renderer, color, x, y + 1, 1, h - 2); + wld_fill_rectangle(drw->renderer, color, x, y - 1, w, 1); + } +} + +int +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) +{ + char buf[1024]; + int ty; + unsigned int ew; + Fnt *usedfont, *curfont, *nextfont; + size_t i, len; + int utf8strlen, utf8charlen, render = x || y || w || h; + long utf8codepoint = 0; + const char *utf8str; + FcCharSet *fccharset; + FcPattern *fcpattern; + FcPattern *match; + FcResult result; + int charexists = 0; + + if (!drw || (render && !drw->scheme) || !text || !drw->fonts) + return 0; + + if (!render) { + w = ~w; + } else { + wld_fill_rectangle(drw->renderer, drw->scheme[invert ? ColFg : ColBg], x, y, w, h); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + while (1) { + utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || wld_font_ensure_char(curfont->wld, utf8codepoint); + if (charexists) { + if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + } else { + nextfont = curfont; + } + break; + } + } + + if (!charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); + /* shorten text if necessary */ + for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) + drw_font_getexts(usedfont, utf8str, len, &ew, NULL); + + if (len) { + memcpy(buf, utf8str, len); + buf[len] = '\0'; + if (len < utf8strlen) + for (i = len; i && i > len - 3; buf[--i] = '.') + ; /* NOP */ + + if (render) { + ty = y + (h - usedfont->wld->height) / 2 + usedfont->wld->ascent; + wld_draw_text(drw->renderer, usedfont->wld, drw->scheme[invert ? ColBg : ColFg], + x, ty, buf, len, NULL); + } + x += ew; + w -= ew; + } + } + + if (!*text) { + break; + } else if (nextfont) { + charexists = 0; + usedfont = nextfont; + } else { + /* Regardless of whether or not a fallback font is found, the + * character must be drawn. */ + charexists = 1; + + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, utf8codepoint); + + if (!drw->fonts->pattern) { + /* Refer to the comment in wldfont_create for more information. */ + die("the first font in the cache must be loaded from a font string."); + } + + fcpattern = FcPatternDuplicate(drw->fonts->pattern); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + match = FcFontMatch(NULL, fcpattern, &result); + + FcCharSetDestroy(fccharset); + FcPatternDestroy(fcpattern); + + if (match) { + usedfont = wldfont_create(drw, NULL, match); + if (usedfont && wld_font_ensure_char(usedfont->wld, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; + } else { + wldfont_free(usedfont); + usedfont = drw->fonts; + } + } + } + } + + return x + (render ? w : 0); +} + +void +drw_map(Drw *drw, struct wl_surface *surface, int x, int y, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + wl_surface_damage(surface, x, y, w, h); + wld_flush(drw->renderer); + wld_swap(drw->surface); +} + +void +drw_sync(Drw *drw) +{ + wld_flush(drw->renderer); + wld_swap(drw->surface); +} + +unsigned int +drw_fontset_getwidth(Drw *drw, const char *text) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); +} + +void +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) +{ + struct wld_extents ext; + + if (!font || !text) + return; + + wld_font_text_extents_n(font->wld, text, len, &ext); + if (w) + *w = ext.advance; + if (h) + *h = font->wld->height; +} diff --git a/drw.h b/drw.h @@ -0,0 +1,48 @@ +/* See LICENSE file for copyright and license details. */ + +typedef struct Fnt { + struct wld_font *wld; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +enum { ColFg, ColBg }; /* Clr scheme index */ +typedef uint32_t Clr; + +typedef struct { + unsigned int w, h; + struct wl_display *dpy; + struct wld_context *ctx; + struct wld_renderer *renderer; + struct wld_surface *surface; + struct wld_font_context *fontctx; + Clr *scheme; + Fnt *fonts; +} Drw; + +/* Drawable abstraction */ +Drw *drw_create(struct wl_display *dpy); +void drw_resize(Drw *drw, struct wl_surface *surface, unsigned int w, unsigned int h); +void drw_free(Drw *drw); + +/* Fnt abstraction */ +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); + +/* Colorscheme abstraction */ +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); +Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); + +/* Drawing context manipulation */ +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); + +/* Drawing functions */ +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); + +/* Map functions */ +void drw_map(Drw *drw, struct wl_surface *surface, int x, int y, unsigned int w, unsigned int h); +void drw_sync(Drw *drw); diff --git a/util.c b/util.c @@ -0,0 +1,63 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} + +char * +estrdup(const char *s) +{ + char *p; + + if (!(p = strdup(s))) + die("strdup:"); + return p; +} + +void +die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} + +void +warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } +} diff --git a/util.h b/util.h @@ -0,0 +1,10 @@ +/* See LICENSE file for copyright and license details. */ + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + +void die(const char *fmt, ...); +void warn(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); +char *estrdup(const char *s);