commit bc38196909d7b6186fffac43012b7f470104ec6c
parent 785a266454e6431df5de95985b436000a688d063
Author: Nihal Jere <nihal@nihaljere.xyz>
Date: Sun, 13 Jun 2021 17:39:20 -0500
working call reception
Diffstat:
A | clients/answer.c | | | 300 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | clients/call.c | | | 330 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | clients/calld.c | | | 192 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- |
A | clients/config.h | | | 4 | ++++ |
A | drw.c | | | 407 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | drw.h | | | 48 | ++++++++++++++++++++++++++++++++++++++++++++++++ |
A | util.c | | | 63 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | util.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, ®listener, 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, ®listener, 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);