Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement interaction #10

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/dtao.1.ronn
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ The `p` and `pa` commands accept the following argument formats:
<pct> percent of the text ascent or descent


### Interaction [UNIMPLEMENTED]
### Interaction

* `^ca(`<btn>`,`<cmd>`)`, `^ca()`:
defines beginning and end of a "clickable area"; when mouse button number <btn> is pressed over the text drawn between these two commands, spawn the command <cmd>
Expand Down
197 changes: 193 additions & 4 deletions dtao.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <string.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <wayland-client.h>
Expand All @@ -34,18 +35,24 @@

/* Includes the newline character */
#define MAX_LINE_LEN 8192
#define MAX_CMD_LEN 256
#define MAX_CLICKABLES 64

enum align { ALIGN_C, ALIGN_L, ALIGN_R };

static struct wl_display *display;
static struct wl_compositor *compositor;
static struct wl_subcompositor *subcompositor;
static struct wl_shm *shm;
static struct zwlr_layer_shell_v1 *layer_shell;

static struct zwlr_layer_surface_v1 *layer_surface;
static struct wl_output *wl_output;
static struct wl_surface *wl_surface;

static struct wl_seat *wl_seat;
static struct wl_pointer *wl_pointer;

static int32_t output = -1;

static uint32_t width, height, titlewidth;
Expand Down Expand Up @@ -135,8 +142,35 @@ parse_color(const char *str, pixman_color_t *clr)
return 0;
}

struct clickable {
uint32_t x1, y1, x2, y2;
uint8_t btn;
char *cmd;
};
Comment on lines +145 to +149
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this after the enum (L38)


static int
parse_clickable_area(char *str, struct clickable *c, uint32_t xpos, uint32_t ypos)
{
c->x1 = xpos;
c->y1 = 0;

c->btn = 0;
while (*str != ',' && *str != '\0')
if (isdigit(*str)) c->btn = c->btn*10 + (*str++ - '0');

if (*str++ == '\0') BARF("bad clickarea cmd");
youbitchoc marked this conversation as resolved.
Show resolved Hide resolved
c->cmd = malloc(strlen(str) + 1);
youbitchoc marked this conversation as resolved.
Show resolved Hide resolved

if (!c->cmd) BARF("bad malloc clickarea cmd");
youbitchoc marked this conversation as resolved.
Show resolved Hide resolved
strcpy(c->cmd, str);
youbitchoc marked this conversation as resolved.
Show resolved Hide resolved
youbitchoc marked this conversation as resolved.
Show resolved Hide resolved
return 0;
}

static struct clickable clickies[MAX_CLICKABLES];
static int clicky_i = 0;
Comment on lines +170 to +171
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this with the rest of global variables.


static char *
handle_cmd(char *cmd, pixman_color_t *bg, pixman_color_t *fg)
handle_cmd(char *cmd, pixman_color_t *bg, pixman_color_t *fg, uint32_t *xpos, uint32_t *ypos)
{
char *arg, *end;

Expand All @@ -158,6 +192,14 @@ handle_cmd(char *cmd, pixman_color_t *bg, pixman_color_t *fg)
} else if (parse_color(arg, fg)) {
fprintf(stderr, "Bad color string \"%s\"\n", arg);
}
} else if (!strcmp(cmd, "ca")) {
if (!*arg) {
clickies[clicky_i].x2 = *xpos;
clickies[clicky_i].y2 = height;
clicky_i++;
} else if (parse_clickable_area(arg, &clickies[clicky_i], *xpos, *ypos)) {
fprintf(stderr, "bad click area \"%s\"\n", arg);
}
} else {
fprintf(stderr, "Unrecognized command \"%s\"\n", cmd);
}
Expand Down Expand Up @@ -220,7 +262,7 @@ draw_frame(char *text)
if (state == UTF8_ACCEPT && *p == '^') {
p++;
if (*p != '^') {
p = handle_cmd(p, &textbgcolor, &textfgcolor);
p = handle_cmd(p, &textbgcolor, &textfgcolor, &xpos, &ypos);
pixman_image_unref(fgfill);
fgfill = pixman_image_create_solid_fill(&textfgcolor);
continue;
Expand Down Expand Up @@ -296,6 +338,15 @@ draw_frame(char *text)
xdraw = (width - maxxpos) / 2;
break;
}

clickies[clicky_i] = (struct clickable) { 0 };

while (clicky_i > 0) {
clicky_i--;
clickies[clicky_i].x1 += xdraw;
clickies[clicky_i].x2 += xdraw;
}

pixman_image_composite32(PIXMAN_OP_OVER, background, NULL, bar, 0, 0, 0, 0,
xdraw, 0, width, height);
pixman_image_composite32(PIXMAN_OP_OVER, foreground, NULL, bar, 0, 0, 0, 0,
Expand Down Expand Up @@ -345,13 +396,138 @@ static struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.closed = layer_surface_closed,
};

struct input_state {
struct wl_surface *surface;
double x, y;
uint32_t button;
};
Comment on lines +407 to +411
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also move this after the enum, (sort alphabetically)


static void
noop() {}
youbitchoc marked this conversation as resolved.
Show resolved Hide resolved

static void
wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface,
wl_fixed_t surface_x, wl_fixed_t surface_y)
{
struct input_state *istate = data;
istate->surface = surface;
istate->x = wl_fixed_to_double(surface_x);
istate->y = wl_fixed_to_double(surface_y);
}
Comment on lines +414 to +422
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I re-read the protocol, and it says:

When a seat's focus enters a surface, the pointer image
is undefined and a client should respond to this event by setting
an appropriate pointer image with the set_cursor request.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's better suited for a separate pr


static void
wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time,
wl_fixed_t surface_x, wl_fixed_t surface_y)
{
struct input_state *istate = data;
istate->x = wl_fixed_to_double(surface_x);
istate->y = wl_fixed_to_double(surface_y);
}

static void
wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
uint32_t time, uint32_t button, uint32_t state)
{
struct input_state *istate = data;
istate->button = state == WL_POINTER_BUTTON_STATE_RELEASED ? 0 : button;
}

static void
wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface)
{
struct input_state *istate = data;
istate->surface = NULL;
}

/* borrowed from dzen's util.c, un-double-forked */
void
spawn(const char *arg) {
static const char *shell = NULL;

if(!shell && !(shell = getenv("SHELL")))
shell = "/bin/sh";
if(!arg)
return;
if(fork() == 0) {
setsid();
execl(shell, shell, "-c", arg, (char *)NULL);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't read the dzen's docs but maybe hardcoding "/bin/sh" is better

fprintf(stderr, "dtao: execl '%s -c %s'", shell, arg);
perror(" failed");
youbitchoc marked this conversation as resolved.
Show resolved Hide resolved
}
wait(0);
youbitchoc marked this conversation as resolved.
Show resolved Hide resolved
}

static void
wl_pointer_frame(void *data, struct wl_pointer *wl_pointer)
{
struct input_state *istate = data;

if (!istate || istate->button == 0)
youbitchoc marked this conversation as resolved.
Show resolved Hide resolved
return;

for (struct clickable *c = clickies; c->btn > 0; c++) {
if (c->btn == istate->button - 271 &&
istate->x >= c->x1 && istate->x <= c->x2 &&
istate->y >= c->y1 && istate->y <= c->y2) {
spawn(c->cmd);
break;
}
}
istate->button = 0;
}

static const struct wl_pointer_listener wl_pointer_listener = {
.enter = wl_pointer_enter,
.leave = wl_pointer_leave,
.motion = wl_pointer_motion,
.button = wl_pointer_button,
.axis = noop,
.axis_source = noop,
.axis_stop = noop,
.axis_discrete = noop,
.frame = wl_pointer_frame,
};

static void
wl_seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities)
{
struct input_state *istate = data;
bool have_pointer = capabilities & WL_SEAT_CAPABILITY_POINTER;

if (have_pointer && wl_pointer == NULL) {
wl_pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(wl_pointer,
&wl_pointer_listener, istate);
} else if (!have_pointer && wl_pointer != NULL) {
wl_pointer_release(wl_pointer);
wl_pointer = NULL;
}
}

static void
wl_seat_name(void *data, struct wl_seat *wl_seat, const char *name)
{
fprintf(stderr, "seat name: %s\n", name);
}

static const struct wl_seat_listener wl_seat_listener = {
.capabilities = wl_seat_capabilities,
.name = wl_seat_name,
};

static void
handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version)
{
struct input_state *istate = data;
if (strcmp(interface, wl_compositor_interface.name) == 0) {
compositor = wl_registry_bind(registry, name,
&wl_compositor_interface, 4);
} else if (strcmp(interface, wl_subcompositor_interface.name) == 0) {
subcompositor = wl_registry_bind(
registry, name, &wl_subcompositor_interface, 1);
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
} else if (strcmp(interface, wl_output_interface.name) == 0) {
Expand All @@ -362,10 +538,17 @@ handle_global(void *data, struct wl_registry *registry,
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
layer_shell = wl_registry_bind(registry, name,
&zwlr_layer_shell_v1_interface, 1);
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, 5);
wl_seat_add_listener(wl_seat, &wl_seat_listener, istate);
}

}

static const struct wl_registry_listener registry_listener = {.global = handle_global,};
static const struct wl_registry_listener registry_listener = {
.global = handle_global,
.global_remove = noop,
};

static void
read_stdin(void)
Expand Down Expand Up @@ -563,7 +746,12 @@ main(int argc, char **argv)
BARF("Failed to create display");

struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);

struct input_state *istate = calloc(1, sizeof(struct input_state));
if (!istate)
BARF("Failed to create inputstate");

wl_registry_add_listener(registry, &registry_listener, istate);
youbitchoc marked this conversation as resolved.
Show resolved Hide resolved
wl_display_roundtrip(display);

if (!compositor || !shm || !layer_shell)
Expand Down Expand Up @@ -609,6 +797,7 @@ main(int argc, char **argv)
wl_compositor_destroy(compositor);
wl_registry_destroy(registry);
wl_display_disconnect(display);
free(istate);

return 0;
}