diff --git a/.gitignore b/.gitignore index ae77015..ab32db2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,6 @@ .projectile dwl-bar -bar -run compile_flags.txt -log.txt +bar.log config.h *-protocol.* diff --git a/Makefile b/Makefile index 321806f..9e30fa7 100644 --- a/Makefile +++ b/Makefile @@ -2,65 +2,71 @@ # dwl-bar # # @file -# @version 1.0 -.POSIX: -.SUFFIXES: - -VERSION = 1.0 +# @version 0.0 +VERSION = 0.0 PKG_CONFIG = pkg-config -#paths +# paths PREFIX = /usr/local MANDIR = $(PREFIX)/share/man +SRCDIR = src -# Compile flags -CC = gcc -PKGS = wayland-client wayland-cursor pangocairo +PKGS = wayland-client wayland-cursor pangocairo +FILES = $(SRCDIR)/main.c $(SRCDIR)/main.h $(SRCDIR)/log.c $(SRCDIR)/log.h \ + $(SRCDIR)/render.c $(SRCDIR)/render.h $(SRCDIR)/event.c $(SRCDIR)/event.h \ + $(SRCDIR)/util.c $(SRCDIR)/util.h $(SRCDIR)/shm.c $(SRCDIR)/shm.h \ + $(SRCDIR)/input.c $(SRCDIR)/input.h $(SRCDIR)/user.c $(SRCDIR)/user.h \ + $(SRCDIR)/bar.c $(SRCDIR)/bar.h $(SRCDIR)/config.h +OBJS = $(SRCDIR)/xdg-output-unstable-v1-protocol.o $(SRCDIR)/xdg-shell-protocol.o \ + $(SRCDIR)/wlr-layer-shell-unstable-v1-protocol.o + +## Compile Flags +CC = gcc BARCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(CFLAGS) BARLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(LIBS) -# Wayland-scanner WAYLAND_SCANNER = `$(PKG_CONFIG) --variable=wayland_scanner wayland-scanner` WAYLAND_PROTOCOLS = `$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols` -srcdir := src all: dwl-bar -dwl-bar: $(srcdir)/xdg-shell-protocol.o $(srcdir)/xdg-output-unstable-v1-protocol.o $(srcdir)/wlr-layer-shell-unstable-v1-protocol.o $(srcdir)/main.c $(srcdir)/bar.c $(srcdir)/shm.c $(srcdir)/config.h +dwl-bar: $(FILES) $(OBJS) $(CC) $^ $(BARLIBS) $(BARCFLAGS) -o $@ -$(srcdir)/%.o: $(srcdir)/%.c $(srcdir)/%.h +$(SRCDIR)/%.o: $(SRCDIR)/%.c $(SRCDIR)/%.h $(CC) -c $< $(BARLIBS) $(BARCFLAGS) -o $@ -$(srcdir)/xdg-shell-protocol.h: +$(SRCDIR)/xdg-shell-protocol.h: $(WAYLAND_SCANNER) client-header \ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ -$(srcdir)/xdg-shell-protocol.c: +$(SRCDIR)/xdg-shell-protocol.c: $(WAYLAND_SCANNER) private-code \ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ -$(srcdir)/xdg-output-unstable-v1-protocol.h: +$(SRCDIR)/xdg-output-unstable-v1-protocol.h: $(WAYLAND_SCANNER) client-header \ $(WAYLAND_PROTOCOLS)/unstable/xdg-output/xdg-output-unstable-v1.xml $@ -$(srcdir)/xdg-output-unstable-v1-protocol.c: +$(SRCDIR)/xdg-output-unstable-v1-protocol.c: $(WAYLAND_SCANNER) private-code \ $(WAYLAND_PROTOCOLS)/unstable/xdg-output/xdg-output-unstable-v1.xml $@ -$(srcdir)/wlr-layer-shell-unstable-v1-protocol.h: +$(SRCDIR)/wlr-layer-shell-unstable-v1-protocol.h: $(WAYLAND_SCANNER) client-header \ protocols/wlr-layer-shell-unstable-v1.xml $@ -$(srcdir)/wlr-layer-shell-unstable-v1-protocol.c: +$(SRCDIR)/wlr-layer-shell-unstable-v1-protocol.c: $(WAYLAND_SCANNER) private-code \ protocols/wlr-layer-shell-unstable-v1.xml $@ -$(srcdir)/config.h: +$(SRCDIR)/config.h: cp src/config.def.h $@ +dev: clean $(SRCDIR)/config.h $(OBJS) + clean: - rm -f dwl-bar src/*.o src/*-protocol.* + rm -f dwl-bar src/config.h src/*.o src/*-protocol.* dist: clean mkdir -p dwl-bar-$(VERSION) - cp -R LICENSE Makefile README.md src patches protocols \ + cp -R LICENSE Makefile README.md dwl-bar.1 src protocols \ dwl-bar-$(VERSION) tar -caf dwl-bar-$(VERSION).tar.gz dwl-bar-$(VERSION) rm -rf dwl-bar-$(VERSION) @@ -69,7 +75,7 @@ install: dwl-bar mkdir -p $(PREFIX)/bin cp -f dwl-bar $(PREFIX)/bin chmod 755 $(PREFIX)/bin/dwl-bar - mkdir -p $(PREFIX)/man1 + mkdir -p $(MANDIR)/man1 cp -f dwl-bar.1 $(MANDIR)/man1 chmod 644 $(MANDIR)/man1/dwl-bar.1 diff --git a/dwl-bar.1 b/dwl-bar.1 index 53d0e6d..cea073b 100644 --- a/dwl-bar.1 +++ b/dwl-bar.1 @@ -13,6 +13,9 @@ prints help message and exits. .TP .B \-v prints version and exits. +.TP +.B \-l +initiates logging .SH USAGE .SS Status .TP diff --git a/src/bar.c b/src/bar.c index fe63655..74d35b8 100644 --- a/src/bar.c +++ b/src/bar.c @@ -1,231 +1,232 @@ -#include -#include -#include -#include -#include -#include - -#include - -#include "config.h" #include "bar.h" -#include "common.h" -#include "pango/pango-font.h" +#include "cairo.h" +#include "config.def.h" +#include "config.h" +#include "input.h" +#include "main.h" +#include "pango/pango-item.h" #include "pango/pango-layout.h" -#include "shm.h" -#include "wlr-layer-shell-unstable-v1-protocol.h" -#include "xdg-shell-protocol.h" +#include "render.h" +#include "user.h" +#include "util.h" +#include "log.h" +#include "pango/pango.h" +#include -#define ELIPSES 3 +static void bar_click(struct Monitor *monitor, void *data, uint32_t button, double x, double y); +static int bar_component_add_elipses(struct BasicComponent *component, struct Pipeline *pipeline, int limit); +static struct BasicComponent *bar_component_create(struct Pipeline *pipeline); +static int bar_component_width(struct BasicComponent *component, struct Pipeline *pipeline); +static void bar_bounds(void *data, double *x, double *y, double *width, double *height); +static enum Clicked bar_get_location(struct Bar *bar, double x, double y, int *tag_index); +static void bar_layout_render(struct Pipeline *pipeline, struct Bar *bar, cairo_t *painter, int *x, int *y); +static void bar_render(struct Pipeline *pipeline, void *data, cairo_t *painter, int *x, int *y); +static void bar_tags_render(struct Pipeline *pipeline, struct Bar *bar, cairo_t *painter, int *x, int *y); +static void bar_title_render(struct Pipeline *pipeline, struct Bar *bar, cairo_t *painter, int *x, int *y); +static void bar_status_render(struct Pipeline *pipeline, struct Bar *bar, cairo_t *painter, int *x, int *y); +static int bar_width(struct Pipeline *pipeline, void *data, unsigned int future_widths); -typedef struct Font { - PangoFontDescription* description; +const struct PipelineListener bar_pipeline_listener = { .render = bar_render, .width = bar_width, }; - uint height; /* This is also the same as lrpad from dwm. */ - uint approx_width; -} Font; +void bar_click(struct Monitor *monitor, void *data, uint32_t button, double x, double y) { + if (!monitor || !data) + return; -typedef struct BarComponent { - PangoLayout* layout; - int x; /* Right bound of box */ -} BarComponent; + struct Bar *bar = data; + const struct Binding *binding; + union Arg *argp = NULL, arg; + int tag_index = -1; + enum Clicked clicked = bar_get_location(bar, x, y, &tag_index); -typedef struct { - uint occupied; - uint focusedClient; /* If the tag has a focused client */ - uint state; - BarComponent component; -} Tag; - -struct Bar { - BarComponent layout, title, status; - Tag tags[9]; - - PangoContext* context; - - /* Colors */ - int background[4], foreground[4]; - - uint invalid; /* So we don't redraw twice. */ - uint active; /* If this bar is on the active monitor */ - uint floating; /* If the focused client is floating */ - - wl_surface* surface; - zwlr_layer_surface_v1* layer_surface; - - Shm* shm; -}; - -static void add_elipses(PangoLayout *layout, int current_size); -static void layerSurface(void* data, zwlr_layer_surface_v1*, uint32_t serial, uint32_t width, uint32_t height); -static void frame(void* data, wl_callback* callback, uint32_t callback_data); -static void bar_render(Bar* bar); -static void bar_tags_render(Bar* bar, cairo_t* painter, int* x); -static void bar_layout_render(Bar* bar, cairo_t* painter, int* x); -static void bar_title_render(Bar* bar, cairo_t* painter, int* x); -static void bar_status_render(Bar* bar, cairo_t* painter, int* x); -static void bar_set_colorscheme(Bar* bar, const int** scheme); -static void set_color(cairo_t* painter, const int rgba[4]); -static void bar_color_background(Bar* bar, cairo_t* painter); -static void bar_color_foreground(Bar* bar, cairo_t* painter); -static Font getFont(void); -static BarComponent bar_component_create(PangoContext* context, PangoFontDescription* description); -static void bar_component_render(Bar* bar, BarComponent* component, cairo_t* painter, uint width, int* x); -static int bar_component_width(BarComponent* component); - -static Font bar_font = {NULL, 0}; - -// So that the compositor can tell us when it's a good time to render again. -const wl_callback_listener frameListener = {.done = frame}; - -// So that wlroots can tell us we need to resize. -// We really only need to worry about this when the bar is visible (sometimes it isn't). -const zwlr_layer_surface_v1_listener layerSurfaceListener = {.configure = layerSurface}; - -void layerSurface(void* data, zwlr_layer_surface_v1* _, uint32_t serial, uint32_t width, uint32_t height) { - Bar* bar = data; - zwlr_layer_surface_v1_ack_configure(bar->layer_surface, serial); - - if (bar->shm) { - if (bar->shm->width == width && bar->shm->height) - return; - shm_destroy(bar->shm); + if (clicked == Click_Tag) { + arg.ui = tag_index; + argp = &arg; } - bar->shm = shm_create(width, height, WL_SHM_FORMAT_XRGB8888); - bar_render(bar); -} + for (int i = 0; i < LENGTH(bindings); i++) { + binding = &bindings[i]; + if (clicked != binding->clicked || button != binding->button) + continue; -void frame(void* data, wl_callback* callback, uint32_t callback_data) { - Bar* bar = data; - bar_render(bar); - wl_callback_destroy(callback); -} - -Font getFont(void) { - PangoFontMap* map = pango_cairo_font_map_get_default(); - if (!map) - die("font map"); - - PangoFontDescription* desc = pango_font_description_from_string(font); - if (!desc) - die("font description"); - - PangoContext* context = pango_font_map_create_context(map); - if (!context) - die("temp context"); - - PangoFont* fnt = pango_font_map_load_font(map, context, desc); - if (!fnt) - die("font load"); - - PangoFontMetrics* metrics = pango_font_get_metrics(fnt, pango_language_get_default()); - if (!metrics) - die("font metrics"); - - Font in = { - desc, - PANGO_PIXELS(pango_font_metrics_get_height(metrics)), - PANGO_PIXELS(pango_font_metrics_get_approximate_char_width(metrics)) - }; - - pango_font_metrics_unref(metrics); - g_object_unref(fnt); - g_object_unref(context); - - return in; -} - -void add_elipses(PangoLayout *layout, int current_size) { - const char *str = pango_layout_get_text(layout); - char *new_str; - int i = 0; - - for (i = strlen(str); (((i+ELIPSES)*bar_font.approx_width)+bar_font.height > current_size - && i >= 0); i--); - - if (i <= 0) { - pango_layout_set_text(layout, "", -1); - } else { - new_str = ecalloc(i+ELIPSES+1, sizeof(char)); - new_str = strncpy(new_str, str, i*sizeof(char)); - new_str[i+1] = '\0'; - new_str = strcat(new_str, "..."); - - pango_layout_set_text(layout, new_str, -1); - free(new_str); + binding->callback(monitor, (argp && !binding->bypass) ? argp : &binding->arg); } } -BarComponent bar_component_create(PangoContext* context, PangoFontDescription* description) { - PangoLayout* layout = pango_layout_new(context); - pango_layout_set_font_description(layout, description); +int bar_component_add_elipses(struct BasicComponent *component, struct Pipeline *pipeline, int limit) { + const char *current_string = pango_layout_get_text(component->layout); + char *new_string; + const int elipses_amnt = 3; + int i; - return (BarComponent){ layout, 0 }; + for (i = strlen(current_string); + (((i+elipses_amnt)*pipeline->font->approx_width)+pipeline->font->height > limit && i >= 0); + i--); + + if (i <= 0) + return 0; + + new_string = strncpy(ecalloc(i+elipses_amnt+1, sizeof(*new_string)), + current_string, i); + new_string[i+1] = '\0'; + new_string = strcat(new_string, "..."); + + pango_layout_set_text(component->layout, new_string, -1); + free(new_string); + return bar_component_width(component, pipeline); } -int bar_component_width(BarComponent* component) { - int w; - pango_layout_get_size(component->layout, &w, NULL); - return PANGO_PIXELS(w); +struct BasicComponent *bar_component_create(struct Pipeline *pipeline) { + if (!pipeline) + return NULL; + + struct BasicComponent *component = basic_component_create(pipeline->context, pipeline->font->description); + component->tx = pipeline->font->height/2.0; + component->ty = 1; + return component; } -void bar_set_colorscheme(Bar* bar, const int** scheme) { - for (int i = 0; i < 4; i++) { - bar->foreground[i] = scheme[0][i]; - bar->background[i] = scheme[1][i]; +int bar_component_width(struct BasicComponent *component, struct Pipeline *pipeline) { + return basic_component_text_width(component) + pipeline->font->height; +} + +void bar_bounds(void *data, double *x, double *y, double *width, double *height) { + struct Bar *bar = data; + int bar_width = 0; + struct Tag *tag; + for (int i = 0; i < LENGTH(tags); i++) { + tag = &bar->tags[i]; + bar_width += tag->component->width; } + bar_width += bar->layout->width; + bar_width += bar->title->width; + bar_width += bar->status->width; + + *x = bar->x; + *y = bar->y; + *width = bar_width; + *height = bar->pipeline->shm->height; } -void set_color(cairo_t* painter, const int rgba[4]) { - cairo_set_source_rgba(painter, rgba[0]/255.0, rgba[1]/255.0, rgba[2]/255.0, rgba[3]/255.0); -} +enum Clicked bar_get_location(struct Bar *bar, double x, double y, int *tag_index) { + enum Clicked clicked = Click_None; + struct Tag *tag; -void bar_color_background(Bar* bar, cairo_t* painter) { - set_color(painter, bar->background); -} - -void bar_color_foreground(Bar* bar, cairo_t* painter) { - set_color(painter, bar->foreground); -} - -void bar_component_render(Bar* bar, BarComponent* component, cairo_t* painter, uint width, int* x) { - pango_cairo_update_layout(painter, component->layout); - component->x = *x+width; - - bar_color_background(bar, painter); - cairo_rectangle(painter, *x, 0, width, bar->shm->height); - cairo_fill(painter); - - bar_color_foreground(bar, painter); - cairo_move_to(painter, *x+(bar_font.height/2.0), 1); - pango_cairo_show_layout(painter, component->layout); -} - -void bar_tags_render(Bar* bar, cairo_t* painter, int* x) { - for ( int i = 0; i < LENGTH(bar->tags); i++ ){ - Tag* tag = &bar->tags[i]; - uint tagWidth = bar_component_width(&tag->component) + bar_font.height; - - /* Creating the tag */ - if (tag->state & TAG_ACTIVE) { - bar_set_colorscheme(bar, schemes[Active_Scheme]); - } else if (tag->state & TAG_URGENT) { - bar_set_colorscheme(bar, schemes[Urgent_Scheme]); - } else { - bar_set_colorscheme(bar, schemes[InActive_Scheme]); + for (int i = 0; i < LENGTH(tags); i++) { + tag = &bar->tags[i]; + if (basic_component_is_clicked(tag->component, x, y)) { + clicked = Click_Tag; + *tag_index = i; + return clicked; } + } - bar_component_render(bar, &tag->component, painter, tagWidth, x); + if (basic_component_is_clicked(bar->layout, x, y)) + clicked = Click_Layout; + else if (basic_component_is_clicked(bar->title, x, y)) + clicked = Click_Title; + else if (basic_component_is_clicked(bar->status, x, y)) + clicked = Click_Status; + + return clicked; +} + +struct Bar *bar_create(struct List *hotspots, struct Pipeline *pipeline) { + if (!pipeline) + return NULL; + + struct Bar *bar = ecalloc(1, sizeof(*bar)); + bar->pipeline = pipeline; + bar->title = bar_component_create(pipeline); + bar->layout = bar_component_create(pipeline); + bar->status = bar_component_create(pipeline); + + char *status = string_create("dwl %.1f", VERSION); + pango_layout_set_text(bar->status->layout, status, strlen(status)); + free(status); + + for (int i = 0; i < LENGTH(tags); i++) { + struct Tag *tag = &bar->tags[i]; + *tag = (struct Tag){ 0, 0, 0, + bar_component_create(pipeline) }; + pango_layout_set_text(bar->tags[i].component->layout, tags[i], strlen(tags[i])); + tag->component->width = basic_component_text_width(tag->component) + pipeline->font->height; + } + + pipeline_add(pipeline, &bar_pipeline_listener, bar); + struct Hotspot *hotspot = list_add(hotspots, ecalloc(1, sizeof(*hotspot))); + hotspot->click = bar_click; + hotspot->bounds = bar_bounds; + hotspot->data = bar; + + bar->x = 0; + bar->y = 0; + + return bar; +} + +void bar_destroy(struct Bar *bar) { + if (!bar) return; + + basic_component_destroy(bar->title); + basic_component_destroy(bar->layout); + basic_component_destroy(bar->status); + for (int i = 0; i < LENGTH(tags); i++) { + struct Tag *tag = &bar->tags[i]; + basic_component_destroy(tag->component); + } + free(bar); +} + +void bar_layout_render(struct Pipeline *pipeline, struct Bar *bar, cairo_t *painter, int *x, int *y) { + if (!bar || !pipeline) + return; + + bar->layout->width = bar_component_width(bar->layout, pipeline); + bar->layout->height = pipeline->shm->height; + pipeline_set_colorscheme(pipeline, schemes[InActive_Scheme]); + basic_component_render(bar->layout, pipeline, painter, x, y); + + *x += bar->layout->width; +} + +void bar_render(struct Pipeline *pipeline, void *data, cairo_t *painter, int *x, int *y) { + if (!pipeline || !data) + return; + + struct Bar *bar = data; + bar->x = *x; + bar->y = *y; + bar_tags_render(pipeline, bar, painter, x, y); + bar_layout_render(pipeline, bar, painter, x, y); + bar_title_render(pipeline, bar, painter, x, y); + bar_status_render(pipeline, bar, painter, x, y); +} + +void bar_tags_render(struct Pipeline *pipeline, struct Bar *bar, cairo_t *painter, int *x, int *y) { + if (!bar || !pipeline) + return; + + for (int i = 0; i < LENGTH(tags); i++) { + struct Tag *tag = &bar->tags[i]; + + if (tag->state & Tag_Active) + pipeline_set_colorscheme(pipeline, schemes[Active_Scheme]); + else if (tag->state & Tag_Urgent) + pipeline_set_colorscheme(pipeline, schemes[Urgent_Scheme]); + else + pipeline_set_colorscheme(pipeline, schemes[InActive_Scheme]); + + tag->component->height = pipeline->shm->height; + basic_component_render(tag->component, pipeline, painter, x, y); if (!tag->occupied) goto done; /* Creating the occupied tag box */ - int boxHeight = bar_font.height / 9, - boxWidth = bar_font.height / 6 + 1; + int boxHeight = pipeline->font->height / 9, + boxWidth = pipeline->font->height / 6 + 1; - if (tag->focusedClient) { + if (tag->has_focused) { cairo_rectangle(painter, *x + boxHeight, boxHeight, boxWidth, boxWidth); cairo_fill(painter); } else { @@ -234,54 +235,33 @@ void bar_tags_render(Bar* bar, cairo_t* painter, int* x) { cairo_stroke(painter); } - done: - *x += tagWidth; +done: + *x += tag->component->width; } } -void bar_layout_render(Bar* bar, cairo_t* painter, int* x) { - if (!bar) +void bar_title_render(struct Pipeline *pipeline, struct Bar *bar, cairo_t *painter, int *x, int *y) { + if (!bar || !pipeline) return; - uint layoutWidth = bar_component_width(&bar->layout) + bar_font.height; + if (bar->active) + pipeline_set_colorscheme(pipeline, schemes[Active_Scheme]); + else + pipeline_set_colorscheme(pipeline, schemes[InActive_Scheme]); - bar_set_colorscheme(bar, schemes[InActive_Scheme]); - bar_component_render(bar, &bar->layout, painter, layoutWidth, x); + bar->title->width = pipeline->shm->width - *x - bar_component_width(bar->status, pipeline) - pipeline_get_future_widths(pipeline); + bar->title->height = pipeline->shm->height; - *x += layoutWidth; -} + if (bar_component_width(bar->title, pipeline) > bar->title->width) + bar->title->width = bar_component_width(bar->title, pipeline); -void bar_title_render(Bar* bar, cairo_t* painter, int* x) { - if (!bar) - return; - - // @HUH: For some reason ww - x - (status width) works, but ww - x - status width doesn't? - int titleWidth = bar->shm->width - bar->layout.x - (bar_component_width(&bar->status) + bar_font.height); - - /* If the status is larger than the title - * or - * a character can't fit in the title. - * Hopefully this helps avoid situations where the title is empty - * and renders but wouldn't if it had text. - */ - if (titleWidth < 0 || bar_font.approx_width+bar_font.height > titleWidth) - return; - - /* If not all text fills the title component - * Then fit as much as possible. - */ - if ((bar_component_width(&bar->title) + bar_font.height) > titleWidth) - add_elipses(bar->title.layout, titleWidth); - - bar->active ? bar_set_colorscheme(bar, schemes[Active_Scheme]) : bar_set_colorscheme(bar, schemes[InActive_Scheme]); - - bar_component_render(bar, &bar->title, painter, titleWidth, x); + basic_component_render(bar->title, pipeline, painter, x, y); if (!bar->floating) goto done; - int boxHeight = bar_font.height / 9, - boxWidth = bar_font.height / 6 + 1; + int boxHeight = pipeline->font->height / 9, + boxWidth = pipeline->font->height / 6 + 1; set_color(painter, grey3); cairo_rectangle(painter, *x + boxHeight + 0.5, boxHeight + 0.5, boxWidth, boxWidth); @@ -289,224 +269,108 @@ void bar_title_render(Bar* bar, cairo_t* painter, int* x) { cairo_stroke(painter); done: - *x += titleWidth; + *x += bar->title->width; } -void bar_status_render(Bar* bar, cairo_t* painter, int* x) { - if (!bar) +void bar_status_render(struct Pipeline *pipeline, struct Bar *bar, cairo_t *painter, int *x, int *y) { + if (!bar || !pipeline) return; - uint statusWidth = bar_component_width(&bar->status) + bar_font.height; + char *previous_status = NULL; - // If the status is as large or larger than the layout then fit as much as we can. - if (statusWidth > (bar->shm->width - bar->layout.x)) - add_elipses(bar->status.layout, (bar->shm->width - bar->layout.x)); - - bar_set_colorscheme(bar, schemes[InActive_Scheme]); + pipeline_set_colorscheme(pipeline, schemes[InActive_Scheme]); if (!bar->active && status_on_active) - bar_set_colorscheme(bar, (const int*[4]){ grey1, grey1 } ); + pipeline_set_colorscheme(pipeline, (const int *[4]){ grey1, grey1 }); - bar_component_render(bar, &bar->status, painter, statusWidth, x); -} + bar->status->width = bar_component_width(bar->status, pipeline); + bar->status->height = pipeline->shm->height; -void bar_render(Bar* bar) { - if (!bar || !bar->shm) - return; - - int x = 0; /* Keep track of the cairo cursor */ - cairo_surface_t* image = cairo_image_surface_create_for_data(shm_data(bar->shm), - CAIRO_FORMAT_ARGB32, - bar->shm->width, - bar->shm->height, - bar->shm->stride); - - cairo_t* painter = cairo_create(image); - pango_cairo_update_context(painter, bar->context); - - bar_tags_render(bar, painter, &x); - bar_layout_render(bar, painter, &x); - bar_title_render(bar, painter, &x); - bar_status_render(bar, painter, &x); - - wl_surface_attach(bar->surface, shm_buffer(bar->shm), 0, 0); - wl_surface_damage(bar->surface, 0, 0, bar->shm->width, bar->shm->height); - wl_surface_commit(bar->surface); - - cairo_destroy(painter); - cairo_surface_destroy(image); - - shm_flip(bar->shm); - bar->invalid = 0; -} - -Bar* bar_create(void) { - Bar* bar = ecalloc(1, sizeof(*bar)); - bar->invalid = 0; - bar->active = 0; - bar->floating = 0; - - bar->context = pango_font_map_create_context(pango_cairo_font_map_get_default()); - if (!bar->context) - die("pango context"); - - if (!bar_font.description) - bar_font = getFont(); - - bar->layout = bar_component_create(bar->context, bar_font.description); - bar->title = bar_component_create(bar->context, bar_font.description); - bar->status = bar_component_create(bar->context, bar_font.description); - - /* Default status */ - char* status = ecalloc(8, sizeof(*status)); - snprintf(status, 8, "dwl %.1f", VERSION); - pango_layout_set_text(bar->status.layout, status, strlen(status)); - free(status); - - for (int i = 0; i < LENGTH(tags); i++) { - BarComponent component = bar_component_create(bar->context, bar_font.description); - pango_layout_set_text(component.layout, tags[i], strlen(tags[i])); - Tag tag = { 0, 0, 0, component }; - bar->tags[i] = tag; + if (bar->status->width > (pipeline->shm->width - *x - pipeline_get_future_widths(pipeline))) { + previous_status = strdup(pango_layout_get_text(bar->status->layout)); + bar->status->width = bar_component_add_elipses(bar->status, pipeline, + (pipeline->shm->width - *x - pipeline_get_future_widths(pipeline))); + if (bar->status->width == 0) { + free(previous_status); + return; + } } - return bar; -} + basic_component_render(bar->status, pipeline, painter, x, y); -void bar_destroy(Bar* bar) { - uint i; - if ( !bar ) - return; - - if ( bar->shm ) - shm_destroy(bar->shm); - - if ( bar->surface ) - wl_surface_destroy(bar->surface); - - if ( bar->layer_surface ) - zwlr_layer_surface_v1_destroy(bar->layer_surface); - - if ( bar->context ) - g_object_unref(bar->context); - - if ( bar->layout.layout ) - g_object_unref(bar->layout.layout); - - if ( bar->status.layout ) - g_object_unref(bar->status.layout); - - if ( bar->title.layout ) - g_object_unref(bar->title.layout); - - for ( i = 0; i < LENGTH(tags); i++) { - Tag tag = bar->tags[i]; - if (tag.component.layout) - g_object_unref(tag.component.layout); + if (previous_status) { + pango_layout_set_text(bar->status->layout, previous_status, -1); + free(previous_status); } - return free(bar); + *x += bar->status->width; } -// When we need to redraw the bar, because of new information or changes. -// We don't just redraw the bar immediately, we will wait for the compositor to say it's ready. -// This is only for if the bar is shown -void bar_invalidate(Bar* bar) { - if ( !bar || bar->invalid || !bar_is_visible(bar)) - return; +void bar_set_active(struct Bar *bar, unsigned int is_active) { + if (!bar) return; - wl_callback* cb = wl_surface_frame(bar->surface); - - wl_callback_add_listener(cb, &frameListener, bar); - wl_surface_commit(bar->surface); - bar->invalid = 1; -} - -void bar_show(Bar* bar, wl_output* output) { - if (!bar || !output || bar_is_visible(bar)) - return; - - bar->surface = wl_compositor_create_surface(compositor); - bar->layer_surface = zwlr_layer_shell_v1_get_layer_surface(shell, bar->surface, output, ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "doom.dwl-bar"); - zwlr_layer_surface_v1_add_listener(bar->layer_surface, &layerSurfaceListener, bar); - - int anchor = bar_top ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - zwlr_layer_surface_v1_set_anchor(bar->layer_surface, - anchor | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); - - int height = bar_font.height + 2; - zwlr_layer_surface_v1_set_size(bar->layer_surface, 0, height); - zwlr_layer_surface_v1_set_exclusive_zone(bar->layer_surface, height); - wl_surface_commit(bar->surface); - -} - -int bar_is_visible(Bar* bar) { - // This is dumb, but I don't know how else to do this. - // We do a negation to convert to boolean int. - // Then another negation to get the right boolean output. - // That is 1 when there is a surface, 0 when there isn't. - return !(!bar->surface); -} - -void bar_set_layout(Bar *bar, const char* text) { - pango_layout_set_text(bar->layout.layout, text, strlen(text)); -} - -void bar_set_title(Bar *bar, const char* text) { - pango_layout_set_text(bar->title.layout, text, strlen(text)); -} - -void bar_set_status(Bar *bar, const char* text) { - pango_layout_set_text(bar->status.layout, text, strlen(text)); -} - -void bar_set_active(Bar* bar, uint is_active) { bar->active = is_active; } -void bar_set_floating(Bar* bar, uint is_floating) { +void bar_set_floating(struct Bar *bar, unsigned int is_floating) { + if (!bar) return; + bar->floating = is_floating; } -void bar_set_tag(Bar *bar, uint i, uint state, uint occupied, uint focusedClient) { - Tag* tag = &bar->tags[i]; - tag->focusedClient = focusedClient; +void bar_set_layout(struct Bar *bar, const char *text) { + if (!bar) return; + + pango_layout_set_text(bar->layout->layout, text, -1); +} + +void bar_set_status(struct Bar *bar, const char *text) { + if (!bar) return; + + pango_layout_set_text(bar->status->layout, text, -1); +} + +void bar_set_tag(struct Bar *bar, unsigned int index, + unsigned int state, unsigned int occupied, unsigned int has_focused) { + if (!bar) return; + + if (!bar || index >= LENGTH(bar->tags) ) return; + + struct Tag *tag = &bar->tags[index]; + tag->has_focused = has_focused; tag->occupied = occupied; tag->state = state; } -wl_surface* bar_get_surface(Bar *bar) { - return bar->surface; +void bar_set_title(struct Bar *bar, const char *text) { + if (!bar) return; + + pango_layout_set_text(bar->title->layout, text, -1); } -void bar_click(Bar* bar, struct Monitor* monitor, int x, int y, uint32_t button) { - Arg *argp = NULL, arg; - Clicked location = Click_None; +int bar_width(struct Pipeline *pipeline, void *data, unsigned int future_widths) { + if (!data || !pipeline) return 0; - if (x < bar->tags[LENGTH(bar->tags)-1].component.x) { - location = Click_Tag; - for (int i = 0; i < LENGTH(bar->tags); i++) { - if (x < bar->tags[i].component.x) { - arg.ui = 1<layout.x) { - location = Click_Layout; - } else if (x < bar->title.x) { - location = Click_Title; - } else { - location = Click_Status; + struct Bar *bar = data; + int width = 0, title_width, status_width; + + for (int i = 0; i < LENGTH(tags); i++) + width += bar_component_width(bar->tags[i].component, pipeline); + width += bar_component_width(bar->layout, pipeline); + + title_width = pipeline->shm->width - width - bar_component_width(bar->status, pipeline) - future_widths; + if (bar_component_width(bar->title, pipeline) > bar->title->width) + title_width = bar_component_width(bar->title, pipeline); + width += title_width; + + status_width = bar_component_width(bar->status, pipeline); + if (status_width > (pipeline->shm->width - width - future_widths)) { + char *previous_status = strdup(pango_layout_get_text(bar->status->layout)); + bar->status->width = bar_component_add_elipses(bar->status, pipeline, + (pipeline->shm->width - width - pipeline_get_future_widths(pipeline))); + pango_layout_set_text(bar->status->layout, previous_status, -1); + free(previous_status); } + width += status_width; - if (location == Click_None) - return; - - for (int i = 0; i < LENGTH(buttons); i++) { - if (buttons[i].location == location && buttons[i].button == button) { - buttons[i].func(monitor, argp ? argp : &buttons[i].arg); - return; - } - } + return width; } diff --git a/src/bar.h b/src/bar.h index 0de06cf..9a97157 100644 --- a/src/bar.h +++ b/src/bar.h @@ -1,35 +1,39 @@ #ifndef BAR_H_ #define BAR_H_ -#include "wlr-layer-shell-unstable-v1-protocol.h" -#include -#include -#include -#include "common.h" -typedef struct Bar Bar; +#include "config.h" +#include "render.h" enum TagState { - None = 0, - Active = 1, - Urgent = 2, + Tag_None = 0, + Tag_Active = 1, + Tag_Urgent = 2, }; -#define TAG_INACTIVE None -#define TAG_ACTIVE Active -#define TAG_URGENT Urgent +struct Tag { + unsigned int occupied, has_focused, state; + struct BasicComponent *component; +}; -Bar* bar_create(void); -void bar_destroy(Bar* bar); -void bar_invalidate(Bar* bar); -void bar_show(Bar* bar, wl_output* output); -int bar_is_visible(Bar* bar); -void bar_click(Bar* bar, struct Monitor* monitor, int x, int y, uint32_t button); -void bar_set_status(Bar* bar, const char* text); -void bar_set_title(Bar* bar, const char* text); -void bar_set_layout(Bar* bar, const char* text); -void bar_set_active(Bar* bar, uint is_active); -void bar_set_floating(Bar* bar, uint is_floating); -void bar_set_tag(Bar* bar, uint tag, uint state, uint occupied, uint focusedClient); -wl_surface* bar_get_surface(Bar* bar); +struct Bar { + struct Pipeline *pipeline; + struct BasicComponent *layout, *title, *status; + struct Tag tags[LENGTH(tags)]; + + unsigned int active, floating; + unsigned int x, y; +}; + +struct Bar *bar_create(struct List *hotspots, struct Pipeline *pipeline); +void bar_destroy(struct Bar *bar); +void bar_set_active(struct Bar *bar, unsigned int is_active); +void bar_set_floating(struct Bar *bar, unsigned int is_floating); +void bar_set_layout(struct Bar *bar, const char *text); +void bar_set_status(struct Bar *bar, const char *text); +void bar_set_tag(struct Bar *bar, unsigned int index, + unsigned int state, unsigned int occupied, unsigned int focusedClient); +void bar_set_title(struct Bar *bar, const char *text); + +extern const struct PipelineListener bar_pipeline_listener; #endif // BAR_H_ diff --git a/src/common.h b/src/common.h deleted file mode 100644 index 3946068..0000000 --- a/src/common.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef COMMON_H_ -#define COMMON_H_ -#include "xdg-output-unstable-v1-protocol.h" -#include -#include -#include -#include -#include -#include - -#define VERSION 1.0 -#define LENGTH(X) (sizeof X / sizeof X[0] ) - -// Commonly used typedefs which makes using these structs easier. -typedef struct wl_registry wl_registry; -typedef struct wl_interface wl_interface; -typedef struct wl_display wl_display; -typedef struct wl_compositor wl_compositor; -typedef struct wl_shm wl_shm; -typedef struct wl_shm_pool wl_shm_pool; -typedef struct wl_buffer wl_buffer; -typedef struct wl_list wl_list; -typedef struct wl_output wl_output; -typedef struct xdg_wm_base xdg_wm_base; -typedef struct zwlr_layer_shell_v1 zwlr_layer_shell_v1; -typedef struct wl_surface wl_surface; -typedef struct zwlr_layer_surface_v1 zwlr_layer_surface_v1; -typedef struct wl_callback wl_callback; -typedef struct wl_callback_listener wl_callback_listener; -typedef struct zwlr_layer_surface_v1_listener zwlr_layer_surface_v1_listener; -typedef struct zxdg_output_manager_v1 zxdg_output_manager_v1; -typedef struct zxdg_output_v1 zxdg_output_v1; -typedef struct wl_seat wl_seat; -typedef struct wl_pointer wl_pointer; -typedef struct wl_keyboard wl_keyboard; -typedef struct wl_array wl_array; -typedef struct wl_cursor_theme wl_cursor_theme; -typedef struct wl_cursor_image wl_cursor_image; -typedef struct pollfd pollfd; -typedef enum wl_shm_format wl_shm_format; - -extern wl_display* display; -extern wl_compositor* compositor; -extern wl_shm* shm; -extern zwlr_layer_shell_v1* shell; - -struct Monitor; - -typedef enum { Click_None, Click_Tag, Click_Layout, Click_Title, Click_Status } Clicked; -typedef enum { InActive_Scheme = 0, Active_Scheme = 1, Urgent_Scheme = 2 } ColorScheme; - -typedef union { - uint ui; - const void* v; -} Arg; - -typedef struct { - Clicked location; - int button; - void (*func)(struct Monitor* monitor, const Arg* arg); - Arg arg; -} Button; - -/* Commonly used functions for allocation and program killing */ -void die(const char* fmt, ...); -void* ecalloc(size_t amnt, size_t size); - -/* - * User function definitions. - * Usually used for when clicking buttons with a pointer. - */ -void spawn(struct Monitor* monitor, const Arg* arg); - -#endif // COMMON_H_ diff --git a/src/config.def.h b/src/config.def.h index a80f77e..ee22d38 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -1,12 +1,14 @@ #ifndef CONFIG_H_ -#define CONFIG_H_ -#include "common.h" +#define CONFIG_H_ + +#include "user.h" +#include #include static const int bar_top = 1; /* Boolean value, non-zero is true. If not top then bottom */ static const int status_on_active = 1; /* Display the status on active monitor only. If not then on all. */ -static const char* font = "Monospace 10"; -static const char* terminal[] = { "alacritty", NULL }; +static const char *font = "Monospace 10"; +static const char *terminal[] = { "alacritty", NULL }; /* * Colors: @@ -24,7 +26,7 @@ static const int grey1[4] = { 34, 34, 34, 255 }; static const int grey2[4] = { 187, 187, 187, 255 }; static const int grey3[4] = { 238, 238, 238, 255 }; -static const int* schemes[3][2] = { +static const int *schemes[3][2] = { /* Scheme Type fg, bg */ [InActive_Scheme] = {grey2, grey1}, [Active_Scheme] = {grey3, cyan}, @@ -34,15 +36,15 @@ static const int* schemes[3][2] = { /* * Tags */ -static const char* tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; /* * Buttons - * Structure is: - * { click location, button clicked (linux event codes), function to run, other argument input (usage varies) } + * See user.h for details on relevant structures. */ -static const Button buttons[] = { -{ Click_Status, BTN_MIDDLE, spawn, {.v = terminal } }, +static const Binding bindings[] = { + /* Click Location, button, callback, bypass, arguments */ + { Click_Status, BTN_MIDDLE, spawn, 0, {.v = terminal } }, }; #endif // CONFIG_H_ diff --git a/src/event.c b/src/event.c new file mode 100644 index 0000000..f546d72 --- /dev/null +++ b/src/event.c @@ -0,0 +1,75 @@ +#include "event.h" +#include "util.h" +#include +#include +#include +#include + +void events_add(struct Events *events, int fd, short mask, void *data, + void (*callback)(int, short, void *)) { + if (!events) + return; + + if (events->pollfds_length == events->pollfds_capacity) { + events->pollfds = realloc(events->pollfds, sizeof(struct pollfd) * (events->pollfds_capacity+1)); + events->pollfds_capacity++; + } + events->pollfds[events->pollfds_length++] = (struct pollfd){fd, mask, 0}; + + struct EventCallback *backcall = list_add(events->callbacks, ecalloc(1, sizeof(*backcall))); + backcall->callback = callback; + backcall->data = data; +} + +struct Events *events_create(void) { + struct Events *events = ecalloc(1, sizeof(*events)); + + events->callbacks = list_create(0); + events->pollfds = NULL; + events->pollfds_length = 0; + events->pollfds_capacity = 0; + + return events; +} + +void events_destroy(struct Events *events) { + if (!events) + return; + + list_elements_destroy(events->callbacks, free); + free(events->pollfds); + free(events); +} + +void events_poll(struct Events *events) { + if (!events) + return; + + if (poll(events->pollfds, events->pollfds_length, -1) <= 0) + return; + + for (int i = 0; i < events->pollfds_length; i++) { + struct pollfd *pollfd = &events->pollfds[i]; + struct EventCallback *callback = events->callbacks->data[i]; + + + if (pollfd->revents & (pollfd->events | POLLHUP | POLLERR)) + callback->callback(pollfd->fd, pollfd->revents, callback->data); + } +} + +void events_remove(struct Events *events, int fd) { + if (!events) + return; + + for (int i = 0; i < events->pollfds_length; i++) { + if (events->pollfds[i].fd != fd) + continue; + + free(list_remove(events->callbacks, i)); + + events->pollfds_length--; + memmove(&events->pollfds[i], &events->pollfds[i+1], + sizeof(struct pollfd) * (events->pollfds_length - i)); + } +} diff --git a/src/event.h b/src/event.h new file mode 100644 index 0000000..1cbee15 --- /dev/null +++ b/src/event.h @@ -0,0 +1,22 @@ +#ifndef EVENT_H_ +#define EVENT_H_ +#include "util.h" + +struct EventCallback { + void (*callback)(int fd, short mask, void *data); + void *data; +}; + +struct Events { + struct List *callbacks; // struct EventCallback* + struct pollfd *pollfds; + int pollfds_length, pollfds_capacity; +}; + +void events_add(struct Events *events, int fd, short mask, void *data, void (*callback)(int fd, short mask, void *data)); +struct Events *events_create(void); +void events_destroy(struct Events *events); +void events_poll(struct Events *events); +void events_remove(struct Events *events, int fd); + +#endif // EVENT_H_ diff --git a/src/input.c b/src/input.c new file mode 100644 index 0000000..2b43e1c --- /dev/null +++ b/src/input.c @@ -0,0 +1,405 @@ +#include "input.h" +#include "log.h" +#include "main.h" +#include "user.h" +#include "util.h" +#include "render.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int button_cmp(const void *left, const void *right); +static void pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value); +static void pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete); +static void pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source); +static void pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis); +static void pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state); +static struct Pointer *pointer_create(struct wl_seat *seat); +static void pointer_destroy(struct Pointer *pointer); +static void 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); +static void pointer_frame(void *data, struct wl_pointer *wl_pointer); +static void pointer_process_scroll(struct Pointer *pointer, unsigned int axis_index); +static void pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface); +static void pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y); +static void pointer_update_cursor(struct Pointer *pointer); +static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities); +static void seat_name(void *data, struct wl_seat *wl_seat, const char *name); +static void touch_cancel(void *data, struct wl_touch *wl_touch); +static struct Touch *touch_create(struct wl_seat *seat); +static void touch_destroy(struct Touch *touch); +static void touch_down(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); +static void touch_frame(void *data, struct wl_touch *wl_touch); +static struct TouchPoint *touch_get_point(struct Touch *touch, int32_t id); +static void touch_motion(void *data, struct wl_touch *wl_touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y); +static void touch_orientation(void *data, struct wl_touch *wl_touch, int32_t id, wl_fixed_t orientation); +static uint32_t touch_point_to_button(struct TouchPoint *point, uint32_t time); +static void touch_shape(void *data, struct wl_touch *wl_touch, int32_t id, wl_fixed_t major, wl_fixed_t minor); +static void touch_up(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, int32_t id); +static uint32_t wl_axis_to_button(int axis, wl_fixed_t value); + +static const struct wl_pointer_listener pointer_listener = { + .axis = pointer_axis, + .axis_discrete = pointer_axis_discrete, + .axis_source = pointer_axis_source, + .axis_stop = pointer_axis_stop, + .button = pointer_button, + .enter = pointer_enter, + .frame = pointer_frame, + .leave = pointer_leave, + .motion = pointer_motion, +}; + +const struct wl_seat_listener seat_listener = { + .capabilities = seat_capabilities, + .name = seat_name, +}; + +static const struct wl_touch_listener touch_listener = { + .cancel = touch_cancel, + .down = touch_down, + .frame = touch_frame, + .motion = touch_motion, + .orientation = touch_orientation, + .shape = touch_shape, + .up = touch_up, +}; + +int button_cmp(const void *left, const void *right) { + return (*(uint32_t*)left) == (*(uint32_t*)right); +} + +void pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis_index, wl_fixed_t value) { + struct Pointer *pointer = data; + struct Axis *axis = &pointer->axis[axis_index]; + + if (axis->discrete_steps == 0 + && time - axis->update_time - SCROLL_TIMEOUT) + axis->value = 0; + + axis->value += value; + axis->update_time = time; + pointer->scrolled = 1; +} + +void pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { + struct Pointer *pointer = data; + + pointer->axis[axis].discrete_steps += abs(discrete); + pointer->scrolled = 1; +} + +void pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) { + /* Nop */ +} + +void pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) { + /* Nop */ +} + +void pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { + struct Pointer *pointer = data; + int index; + + if (state == WL_POINTER_BUTTON_STATE_PRESSED && list_cmp_find(pointer->buttons, &button, button_cmp) == -1) { + uint32_t *btn = list_add(pointer->buttons, ecalloc(1, sizeof(*btn))); + *btn = button; + } + else if (state == WL_POINTER_BUTTON_STATE_RELEASED && + (index = list_cmp_find(pointer->buttons, &button, button_cmp)) != -1) { + free(list_remove(pointer->buttons, index)); + } +} + +struct Pointer *pointer_create(struct wl_seat *seat) { + if (!seat) return NULL; + + struct Pointer *pointer = ecalloc(1, sizeof(*pointer)); + pointer->pointer = wl_seat_get_pointer(seat); + pointer->scrolled = 0; + pointer->buttons = list_create(0); + pointer->focused_monitor = NULL; + pointer->cursor_surface = NULL; + pointer->cursor_image = NULL; + pointer->cursor_theme = NULL; + + return pointer; +} + +void pointer_destroy(struct Pointer *pointer) { + if (!pointer) return; + + wl_pointer_release(pointer->pointer); + wl_surface_destroy(pointer->cursor_surface); + wl_cursor_theme_destroy(pointer->cursor_theme); + list_elements_destroy(pointer->buttons, free); + free(pointer); +} + +void 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 Pointer *pointer = data; + pointer->focused_monitor = monitor_from_surface(surface); + if (!pointer->focused_monitor) + return; + + pointer_update_cursor(pointer); + wl_pointer_set_cursor(wl_pointer, serial, pointer->cursor_surface, + pointer->cursor_image->hotspot_x, pointer->cursor_image->hotspot_y); +} + +void pointer_frame(void *data, struct wl_pointer *wl_pointer) { + struct Pointer *pointer = data; + struct Monitor *monitor = pointer->focused_monitor; + if (!monitor) return; + + for (int i = 0; i < pointer->buttons->length; i++) + process_hotspots(pointer->focused_monitor, pointer->x, pointer->y, + *(uint32_t*)pointer->buttons->data[i]); + list_elements_destroy(pointer->buttons, free); + pointer->buttons = list_create(0); + + if (pointer->scrolled) { + for (int i = 0; i < 2; i++) + pointer_process_scroll(pointer, i); + } +} + +void pointer_process_scroll(struct Pointer *pointer, unsigned int axis_index) { + struct Axis *axis = &pointer->axis[axis_index]; + if (axis->discrete_steps) { + for (int i = 0; i < axis->discrete_steps; i++) + process_hotspots(pointer->focused_monitor, pointer->x, pointer->y, wl_axis_to_button(axis_index, axis->value)); + } else { + while (abs(axis->value) > SCROLL_THRESHOLD) { + if (axis->value > 0){ + process_hotspots(pointer->focused_monitor, pointer->x, pointer->y, wl_axis_to_button(axis_index, SCROLL_THRESHOLD)); + axis->value -= SCROLL_THRESHOLD; + } else { + process_hotspots(pointer->focused_monitor, pointer->x, pointer->y, wl_axis_to_button(axis_index, -SCROLL_THRESHOLD)); + axis->value += SCROLL_THRESHOLD; + } + } + } +} + +void pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) { + struct Pointer *pointer = data; + pointer->focused_monitor = NULL; +} + +void pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { + struct Pointer *pointer = data; + pointer->x = wl_fixed_to_double(surface_x); + pointer->y = wl_fixed_to_double(surface_y); +} + +void pointer_update_cursor(struct Pointer *pointer) { + if (!pointer) + return; + + if (!pointer->cursor_surface) + pointer->cursor_surface = wl_compositor_create_surface(compositor); + + if (pointer->cursor_theme) + wl_cursor_theme_destroy(pointer->cursor_theme); + + unsigned int cursor_size = 24; + const char *cursor_theme = getenv("XCURSOR_THEME"); + const char *env_cursor_size = getenv("XCURSOR_SIZE"); + if (env_cursor_size && strlen(env_cursor_size) > 0) { + errno = 0; + char *end; + unsigned int size = strtoul(env_cursor_size, &end, 10); + if (!*end && errno == 0) + cursor_size = size; + } + pointer->cursor_theme = wl_cursor_theme_load(cursor_theme, cursor_size, shm); + pointer->cursor_image = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr")->images[0]; + wl_surface_attach(pointer->cursor_surface, wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0); + wl_surface_commit(pointer->cursor_surface); +} + +void process_hotspots(struct Monitor* monitor, double x, double y, uint32_t button) { + struct Hotspot *hotspot; + for (int i = 0; i < monitor->hotspots->length; i++) { + hotspot = monitor->hotspots->data[i]; + + double hotspot_x = 0, hotspot_y = 0, hotspot_width = 0, hotspot_height = 0; + hotspot->bounds(hotspot->data, &hotspot_x, &hotspot_y, &hotspot_width, &hotspot_height); + + if (!( x > hotspot_x && y > hotspot_y && + x < (hotspot_x+hotspot_width) && y < (hotspot_y+hotspot_height))) + continue; + + hotspot->click(monitor, hotspot->data, button, x, y); + return; + } +} + +void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) { + struct Seat *seat = data; + int has_pointer = capabilities & WL_SEAT_CAPABILITY_POINTER, + has_touch = capabilities & WL_SEAT_CAPABILITY_TOUCH; + + if (!seat->pointer && has_pointer) { + seat->pointer = pointer_create(seat->seat); + wl_pointer_add_listener(seat->pointer->pointer, &pointer_listener, seat->pointer); + } + else if (seat->pointer && !has_pointer) { + pointer_destroy(seat->pointer); + } + + if (!seat->touch && has_touch) { + seat->touch = touch_create(seat->seat); + wl_touch_add_listener(seat->touch->touch, &touch_listener, seat->touch); + } + else if (seat->touch && !has_touch) { + touch_destroy(seat->touch); + } +} + +void seat_destroy(struct Seat *seat) { + if (!seat) return; + + pointer_destroy(seat->pointer); + touch_destroy(seat->touch); + wl_seat_release(seat->seat); + free(seat); +} + +void seat_name(void *data, struct wl_seat *wl_seat, const char *name) { + /* Nop */ +} + +void touch_cancel(void *data, struct wl_touch *wl_touch) { + struct Touch *touch = data; + struct TouchPoint *point; + for (int i = 0; i < LENGTH(touch->points); i++) { + point = &touch->points[i]; + point->id = -1; + point->focused_monitor = NULL; + } +} + +struct Touch *touch_create(struct wl_seat *seat) { + if (!seat) return NULL; + + struct Touch *touch = ecalloc(1, sizeof(*touch)); + touch->touch = wl_seat_get_touch(seat); + struct TouchPoint *point; + for (int i = 0; i < LENGTH(touch->points); i++) { + point = &touch->points[i]; + point->id = -1; + point->focused_monitor = NULL; + } + + return touch; +} + +void touch_destroy(struct Touch *touch) { + if (!touch) return; + + wl_touch_release(touch->touch); + free(touch); +} + +void touch_down(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) { + struct Touch *touch = data; + struct TouchPoint *point = touch_get_point(touch, id); + if (!point) + return; + + point->focused_monitor = monitor_from_surface(surface); + if (!point->focused_monitor) + return; + + point->id = id; + point->time = time; + point->start_x = wl_fixed_to_double(x); + point->start_y = wl_fixed_to_double(y); +} + +void touch_frame(void *data, struct wl_touch *wl_touch) { + /* Nop */ +} + +struct TouchPoint *touch_get_point(struct Touch *touch, int32_t id) { + struct TouchPoint *point; + int empty_index = -1; + for (int i = 0; i < LENGTH(touch->points); i++) { + point = &touch->points[i]; + if (point->id == id) { + return point; + } + if (!point->focused_monitor && point->id == -1) + empty_index = i; + } + + if (empty_index == -1) + return NULL; + + return &touch->points[empty_index]; +} + +void touch_motion(void *data, struct wl_touch *wl_touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) { + struct Touch *touch = data; + struct TouchPoint *point = touch_get_point(touch, id); + if (!point) + return; + + point->time = time; + point->x = wl_fixed_to_double(x); + point->y = wl_fixed_to_double(y); +} + +void touch_orientation(void *data, struct wl_touch *wl_touch, int32_t id, wl_fixed_t orientation) { + /* Nop */ +} + +uint32_t touch_point_to_button(struct TouchPoint *point, uint32_t time) { + /* "progress" is a measure from 0..100 representing the fraction of the + * output the touch gesture has travelled, positive when moving to the right + * and negative when moving to the left. */ + int progress = (int)((point->x - point->start_x) / point->focused_monitor->pipeline->shm->width * 100); + if (abs(progress) > 20) + return (progress > 0 ? Gesture_Right : Gesture_Left); + + if (time - point->time < 500) + return BTN_LEFT; + if (time - point->time < 1000) + return BTN_RIGHT; + + /* If hold time is longer than 1 second then it is a middle click. */ + return BTN_MIDDLE; +} + +void touch_shape(void *data, struct wl_touch *wl_touch, int32_t id, wl_fixed_t major, wl_fixed_t minor) { + /* Nop */ +} + +void touch_up(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, int32_t id) { + struct Touch *touch = data; + struct TouchPoint *point = touch_get_point(touch, id); + if (!point) return; + + uint32_t button = touch_point_to_button(point, time); + process_hotspots(point->focused_monitor, point->x, point->y, button); +} + +uint32_t wl_axis_to_button(int axis, wl_fixed_t value) { + int negative = wl_fixed_to_double(value) < 0; + switch (axis) { + case WL_POINTER_AXIS_VERTICAL_SCROLL: + return negative ? Scroll_Up : Scroll_Down; + case WL_POINTER_AXIS_HORIZONTAL_SCROLL: + return negative ? Scroll_Left : Scroll_Right; + default: + return 0; + } +} diff --git a/src/input.h b/src/input.h new file mode 100644 index 0000000..4603829 --- /dev/null +++ b/src/input.h @@ -0,0 +1,65 @@ +#ifndef INPUT_H_ +#define INPUT_H_ + +#include "main.h" +#include "util.h" +#include "user.h" +#include +#include + +#define SCROLL_TIMEOUT 1000 +#define SCROLL_THRESHOLD 10000 + +struct TouchPoint { + int32_t id; + uint32_t time; + struct Monitor *focused_monitor; + double start_x, start_y, + x, y; +}; + +struct Touch { + struct wl_touch *touch; + struct TouchPoint points[16]; +}; + +struct Axis { + wl_fixed_t value; + uint32_t discrete_steps, update_time; +}; + +struct Pointer { + struct wl_pointer *pointer; + struct Monitor *focused_monitor; + + struct wl_cursor_theme *cursor_theme; + struct wl_cursor_image *cursor_image; + struct wl_surface *cursor_surface; + + double x, y; + struct List *buttons; /* uint32_t* */ + struct Axis axis[2]; + int scrolled; +}; + +struct Seat { + uint32_t wl_name; + struct wl_seat *seat; + struct Pointer *pointer; + struct Touch *touch; + + struct wl_list link; +}; + +struct Hotspot { + void (*click)(struct Monitor *monitor, void *data, uint32_t button, double x, double y); + void (*bounds)(void *data, double *x, double *y, double *width, double *height); + void *data; +}; + +extern const struct wl_seat_listener seat_listener; + +void process_hotspots(struct Monitor* monitor, double x, double y, uint32_t button); +void seat_destroy(struct Seat *seat); + +#endif // INPUT_H_ diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..f3a4650 --- /dev/null +++ b/src/log.c @@ -0,0 +1,44 @@ +#include "log.h" +#include +#include +#include + +FILE *log_file = NULL; + +void bar_log(enum LogLevel level, const char *fmt, ...) { + if (!log_file) + return; + + va_list ap; + va_start(ap, fmt); + switch (level) { + case LOG_INFO: + fprintf(log_file, "[dwl-bar] info: "); + break; + case LOG_ERROR: + fprintf(log_file, "[dwl-bar] error: "); + break; + } + vfprintf(log_file, fmt, ap); + va_end(ap); + fputc('\n', log_file); + + fflush(log_file); +} + +void log_destroy(void) { + if (!log_file) + return; + + fclose(log_file); +} + +int setup_log(void) { + log_file = fopen("bar.log", "w"); + if (!log_file) + return 0; + + bar_log(LOG_INFO, "Setup Logging"); + + return 1; +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..cd40c84 --- /dev/null +++ b/src/log.h @@ -0,0 +1,10 @@ +#ifndef LOG_H_ +#define LOG_H_ + +enum LogLevel { LOG_INFO, LOG_ERROR }; + +void bar_log(enum LogLevel level, const char *fmt, ...); +void log_destroy(void); +int setup_log(void); + +#endif // LOG_H_ diff --git a/src/main.c b/src/main.c index 53d2e96..56877f4 100644 --- a/src/main.c +++ b/src/main.c @@ -1,865 +1,556 @@ +#include "bar.h" +#include "event.h" +#include "log.h" +#include "render.h" +#include "util.h" +#include "main.h" +#include "input.h" +#include "xdg-output-unstable-v1-protocol.h" +#include "xdg-shell-protocol.h" +#include "wlr-layer-shell-unstable-v1-protocol.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "bar.h" -#include "common.h" -#include "config.h" -#include "shm.h" -#include "wlr-layer-shell-unstable-v1-protocol.h" -#include "xdg-output-unstable-v1-protocol.h" -#include "xdg-shell-protocol.h" - -/* - * When checking to see if two strings are the same with strcmp, - * 0 means they are the same, otherwise they are different. - */ -#define EQUAL 0 -#define POLLFDS 4 - -typedef struct Monitor { - char *xdg_name; - uint32_t registry_name; - - wl_output *output; - Bar *bar; - wl_list link; -} Monitor; - -typedef struct { - wl_pointer* pointer; - Monitor* focused_monitor; - - int x, y; - uint32_t* buttons; - uint size; -} Pointer; - -typedef struct { - uint32_t registry_name; - wl_seat* seat; - wl_list link; - - Pointer* pointer; -} Seat; - -typedef struct { - wl_output *output; - uint32_t name; - wl_list link; -} uninitOutput; - -typedef struct { - wl_registry *registry; - uint32_t name; - const char *interface_str; -} HandleGlobal; - +static void check_global(void *global, const char *name); +static void check_globals(void); static void cleanup(void); -static void setup(void); -static void run(void); -static void globalChecks(void); -static void checkGlobal(void *global, const char *name); -static void monitorSetup(uint32_t name, wl_output *output); -static void set_cloexec(int fd); -static void sighandler(int _); -static void flush(void); -static void setupFifo(void); -static char* to_delimiter(char* string, ulong *start_end, char delimiter); -static char* get_line(int fd); -static void on_status(void); -static void on_stdin(void); -static void handle_stdin(char* line); -static Monitor* monitor_from_name(char* name); -static Monitor* monitor_from_surface(wl_surface* surface); -static void update_monitor(Monitor* monitor); - -/* Register listener related functions */ -static void onGlobalAdd(void *data, wl_registry *registry, uint32_t name, +static void display_in(int fd, short mask, void *data); +static void fifo_handle(const char *line); +static void fifo_in(int fd, short mask, void *data); +static void fifo_setup(void); +static void monitor_destroy(struct Monitor *monitor); +static struct Monitor *monitor_from_name(const char *name); +struct Monitor *monitor_from_surface(const struct wl_surface *surface); +static void monitor_update(struct Monitor *monitor); +static void pipe_in(int fd, short mask, void *data); +static void registry_global_add(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version); -static void onGlobalRemove(void *data, wl_registry *registry, uint32_t name); -static int regHandle(void **store, HandleGlobal helper, - const wl_interface *interface, int version); +static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name); +static void run(void); +static void set_cloexec(int fd); +static void setup(void); +static void stdin_handle(const char *line); +static void stdin_in(int fd, short mask, void *data); +static void sigaction_handler(int _); +static void xdg_output_name(void *data, struct zxdg_output_v1 *output, const char *name); +static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial); -/* xdg listener members */ -static void ping(void *data, xdg_wm_base *xdg_wm_base, uint32_t serial); -static void name(void *data, zxdg_output_v1 *output, const char *name); - -/* seat listener member */ -static void capabilites(void* data, wl_seat* wl_seat, uint32_t capabilities); - -/* pointer listener members */ -static void enter(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface, wl_fixed_t x, wl_fixed_t y); -static void leave(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface); -static void motion(void* data, wl_pointer* pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y); -static void button(void* data, wl_pointer* pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state); -static void frame(void* data, wl_pointer* pointer); - -/* Also pointer listener members, but we don't do anything in these functions */ -static void axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {} -static void axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) {} -static void axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) {} -static void axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {} -static void seat_name(void* data, wl_seat* seat, const char* name) {} - -/* Globals */ -wl_display *display; -wl_compositor *compositor; -wl_shm *shm; -zwlr_layer_shell_v1 *shell; -static xdg_wm_base *base; -static zxdg_output_manager_v1 *output_manager; - -/* Cursor */ -static wl_cursor_image* cursor_image; -static wl_surface* cursor_surface; - -/* Lists */ -static wl_list seats; -static wl_list monitors; -static wl_list uninitializedOutputs; - -/* File Descriptors */ -static pollfd *pollfds; -static int wl_display_fd; -static int self_pipe[2]; +static struct xdg_wm_base *base; +struct wl_compositor *compositor; +static struct wl_display *display; +static int display_fd; +static struct Events *events; static int fifo_fd; -static char* fifo_path; - -/* Sigactions */ -static struct sigaction sighandle; -static struct sigaction child_handle; - -/* - * So that the global handler knows that we can initialize an output. - * Rather than just store it for when we have all of our globals. - * - * Since wayland is asynchronous we may get our outputs before we're ready for - * them. - */ -static int ready = 0; - +static char *fifo_path; +static struct wl_list monitors; // struct Monitor* +static struct zxdg_output_manager_v1 *output_manager; static const struct wl_registry_listener registry_listener = { - .global = onGlobalAdd, - .global_remove = onGlobalRemove, + .global = registry_global_add, + .global_remove = registry_global_remove, }; - -static const struct xdg_wm_base_listener xdg_wm_base_listener = { - .ping = ping, -}; - -/* So that we can get the monitor names to match with dwl monitor names. */ +static int running = 0; +static struct wl_list seats; // struct Seat* +static int self_pipe[2]; +struct zwlr_layer_shell_v1 *shell; +struct wl_shm *shm; static const struct zxdg_output_v1_listener xdg_output_listener = { - .name = name, + .name = xdg_output_name, +}; +static const struct xdg_wm_base_listener xdg_wm_base_listener = { + .ping = xdg_wm_base_ping, }; -static const struct wl_pointer_listener pointer_listener = { - .enter = enter, - .leave = leave, - .motion = motion, - .button = button, - .frame = frame, - - .axis = axis, - .axis_discrete = axis_discrete, - .axis_source = axis_source, - .axis_stop = axis_stop, -}; - -static const struct wl_seat_listener seat_listener = { - .capabilities = capabilites, - .name = seat_name, -}; - -void enter(void *data, wl_pointer *pointer, uint32_t serial, - wl_surface *surface, wl_fixed_t x, wl_fixed_t y) { - Seat seat = *(Seat*)data; - seat.pointer->focused_monitor = monitor_from_surface(surface); - - if (!cursor_image) { - wl_cursor_theme *theme = wl_cursor_theme_load(NULL, 24, shm); - cursor_image = wl_cursor_theme_get_cursor(theme, "left_ptr")->images[0]; - cursor_surface = wl_compositor_create_surface(compositor); - wl_surface_attach(cursor_surface, wl_cursor_image_get_buffer(cursor_image), 0, 0); - wl_surface_commit(cursor_surface); - } - - wl_pointer_set_cursor(pointer, serial, cursor_surface, - cursor_image->hotspot_x, cursor_image->hotspot_y); +void check_global(void *global, const char *name) { + if (global) + return; + panic("Wayland compositor did not export: %s", name); } -void leave(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface) { - Seat seat = *(Seat*)data; - seat.pointer->focused_monitor = NULL; -} - -void motion(void* data, wl_pointer* pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) { - Seat seat = *(Seat*)data; - seat.pointer->x = wl_fixed_to_int(x); - seat.pointer->y = wl_fixed_to_int(y); -} - -void button(void *data, wl_pointer *pointer, uint32_t serial, uint32_t time, - uint32_t button, uint32_t state) { - Seat seat = *(Seat*)data; - uint32_t* new_buttons = NULL; - int i, prev = -1; /* The index of this button */ - - for (i = 0; i < seat.pointer->size; i++) { - if (seat.pointer->buttons[i] == button) - prev = i; - } - - /* If this button was newly pressed. */ - if (state == WL_POINTER_BUTTON_STATE_PRESSED && prev == -1) { - new_buttons = ecalloc(seat.pointer->size+1, sizeof(uint32_t)); - for (i = 0; i < seat.pointer->size+1; i++) { - if (i == seat.pointer->size) { - new_buttons[i] = button; - break; - } - - new_buttons[i] = seat.pointer->buttons[i]; - } - - seat.pointer->size++; - } - - /* If this button was released and we have it. */ - if(state == WL_KEYBOARD_KEY_STATE_RELEASED && prev != -1) { - new_buttons = ecalloc(seat.pointer->size-1, sizeof(uint32_t)); - for (i = 0; i < seat.pointer->size; i++) { - if (i == prev) - continue; - - if (i < prev) - new_buttons[i] = seat.pointer->buttons[i]; - - if (i > prev) - new_buttons[i-1] = seat.pointer->buttons[i]; - } - - seat.pointer->size--; - } - - free(seat.pointer->buttons); - seat.pointer->buttons = new_buttons; - return; -} - -void frame(void* data, wl_pointer* pointer) { - Seat seat = *(Seat*)data; - Monitor* monitor = seat.pointer->focused_monitor; - if (!monitor) - return; - - for (int i = 0; i < seat.pointer->size; i++) - bar_click(monitor->bar, monitor, seat.pointer->x, seat.pointer->y, seat.pointer->buttons[i]); - - free(seat.pointer->buttons); - seat.pointer->buttons = NULL; - seat.pointer->size = 0; -} - -void capabilites(void* data, wl_seat* wl_seat, uint32_t capabilities) { - Seat* seat = data; - int has_pointer = capabilities & WL_SEAT_CAPABILITY_POINTER; - if (!seat->pointer && has_pointer) { - seat->pointer = ecalloc(1, sizeof(Pointer)); - seat->pointer->pointer = wl_seat_get_pointer(wl_seat); - seat->pointer->buttons = NULL; - seat->pointer->size = 0; - wl_pointer_add_listener(seat->pointer->pointer, &pointer_listener, seat); - return; - } - - if (seat->pointer && !has_pointer) { - wl_pointer_release(seat->pointer->pointer); - seat->pointer->focused_monitor = NULL; - if (seat->pointer->buttons) - free(seat->pointer->buttons); - - free(seat->pointer); - } -} - -void name(void *data, zxdg_output_v1 *xdg_output, const char *name) { - Monitor *monitor = data; - monitor->xdg_name = strdup(name); - zxdg_output_v1_destroy(xdg_output); -} - -void ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial) { - xdg_wm_base_pong(base, serial); -} - -int regHandle(void **store, HandleGlobal helper, const wl_interface *interface, - int version) { - if (strcmp(helper.interface_str, interface->name) != EQUAL) - return 0; - - *store = wl_registry_bind(helper.registry, helper.name, interface, version); - return 1; -} - -void onGlobalAdd(void *_, wl_registry *registry, uint32_t name, - const char *interface, uint32_t version) { - - HandleGlobal helper = {registry, name, interface}; - if (regHandle((void **)&compositor, helper, &wl_compositor_interface, 4)) - return; - if (regHandle((void **)&shm, helper, &wl_shm_interface, 1)) - return; - if (regHandle((void **)&output_manager, helper, &zxdg_output_manager_v1_interface, 3)) - return; - if (regHandle((void **)&shell, helper, &zwlr_layer_shell_v1_interface, 4)) - return; - if (regHandle((void **)&base, helper, &xdg_wm_base_interface, 2)) { - xdg_wm_base_add_listener(base, &xdg_wm_base_listener, NULL); - return; - } - - { - wl_output *output; - if (regHandle((void **)&output, helper, &wl_output_interface, 1)) { - if (ready) { - monitorSetup(name, output); - } else { - uninitOutput *uninit = ecalloc(1, sizeof(uninitOutput)); - uninit->output = output; - uninit->name = name; - wl_list_insert(&uninitializedOutputs, &uninit->link); - } - return; - } - } - - { - wl_seat* seat; - if (regHandle((void**)&seat, helper, &wl_seat_interface, 7)) { - Seat* seat_ = ecalloc(1, sizeof(*seat_)); - seat_->seat = seat; - seat_->registry_name = name; - wl_list_insert(&seats, &seat_->link); - wl_seat_add_listener(seat, &seat_listener, seat_); - return; - } - } -} - -void onGlobalRemove(void *_, wl_registry *registry, uint32_t name) { - /* Deconstruct a monitor when it disappears */ - Monitor *current_monitor, *tmp_monitor; - wl_list_for_each_safe(current_monitor, tmp_monitor, &monitors, link) { - if (current_monitor->registry_name == name) { - wl_list_remove(¤t_monitor->link); - bar_destroy(current_monitor->bar); - } - } - - /* Deconstruct seat when it disappears */ - Seat *seat, *tmp_seat; - wl_list_for_each_safe(seat, tmp_seat, &seats, link) { - if (seat->registry_name == name) { - seat->pointer->focused_monitor = NULL; - wl_pointer_release(seat->pointer->pointer); - wl_list_remove(&seat->link); - free(seat->pointer->buttons); - free(seat->pointer); - free(seat); - } - } -} - -void spawn(Monitor* monitor, const Arg *arg) { - if (fork() != 0) - return; - - char* const* argv = arg->v; - setsid(); - execvp(argv[0], argv); - fprintf(stderr, "bar: execvp %s", argv[0]); - perror(" failed\n"); - exit(1); -} - -void checkGlobal(void *global, const char *name) { - if (global) - return; - fprintf(stderr, "Wayland server did not export %s\n", name); - cleanup(); - exit(1); -} - -/* - * We just check and make sure we have our needed globals if any fail then we - * exit. - */ -void globalChecks() { - checkGlobal(compositor, "wl_compositor"); - checkGlobal(shm, "wl_shm"); - checkGlobal(base, "xdg_wm_base"); - checkGlobal(shell, "wlr_layer_shell"); - checkGlobal(output_manager, "zxdg_output_manager"); - - ready = 1; -} - -void monitorSetup(uint32_t name, wl_output *output) { - Monitor *monitor = ecalloc(1, sizeof(*monitor)); - - monitor->bar = bar_create(); - monitor->output = output; - monitor->registry_name = name; - - wl_list_insert(&monitors, &monitor->link); - - // So we can get the monitor name. - zxdg_output_v1 *xdg_output = zxdg_output_manager_v1_get_xdg_output(output_manager, output); - zxdg_output_v1_add_listener(xdg_output, &xdg_output_listener, monitor); -} - -void set_cloexec(int fd) { - int flags = fcntl(fd, F_GETFD); - if (flags == -1) - die("FD_GETFD"); - if (fcntl(fd, flags | FD_CLOEXEC) < 0) - die("FDD_SETFD"); -} - -void sighandler(int _) { - if (write(self_pipe[1], "0", 1) < 0) - die("sighandler"); -} - -void flush(void) { - wl_display_dispatch_pending(display); - if (wl_display_flush(display) < 0 && errno == EAGAIN) { - for (int i = 0; i < POLLFDS; i++) { - struct pollfd *poll = &pollfds[i]; - if (poll->fd == wl_display_fd) - poll->events |= POLLOUT; - } - } -} - -void setupFifo(void) { - int result, fd, i; - char *runtime_path = getenv("XDG_RUNTIME_DIR"), *file_name, *path; - - for (i = 0; i < 100; i++) { - file_name = ecalloc(12, sizeof(*file_name)); - sprintf(file_name, "/dwl-bar-%d", i); - - path = ecalloc(strlen(runtime_path)+strlen(file_name)+1, sizeof(char)); - strcat(path, runtime_path); - strcat(path, file_name); - free(file_name); - - result = mkfifo(path, 0666); - if (result < 0) { - if (errno != EEXIST) - die("mkfifo"); - - free(path); - continue; - } - - if ((fd = open(path, O_CLOEXEC | O_RDONLY | O_NONBLOCK)) < 0) - die("open fifo"); - - fifo_path = path; - fifo_fd = fd; - - return; - } - - die("setup fifo"); /* If we get here then we couldn't setup the fifo */ -} - -void update_monitor(Monitor* monitor) { - if (!bar_is_visible(monitor->bar)) { - bar_show(monitor->bar, monitor->output); - return; - } - - bar_invalidate(monitor->bar); - return; -} - -Monitor* monitor_from_name(char* name) { - Monitor* monitor; - wl_list_for_each(monitor, &monitors, link) { - if (strcmp(name, monitor->xdg_name) == EQUAL) - return monitor; - } - - return NULL; -} - -Monitor* monitor_from_surface(wl_surface* surface) { - Monitor* monitor; - wl_list_for_each(monitor, &monitors, link) { - if (surface == bar_get_surface(monitor->bar)) - return monitor; - } - - return NULL; -} - -/* - * Parse and extract a substring based on a delimiter - * start_end is a ulong that we will use to base our starting location. - * Then replace as the end point to be used later on. - */ -char* to_delimiter(char* string, ulong *start_end, char delimiter) { - char* output; - ulong i, len = strlen(string); - - if (*start_end > len) - return NULL; - - for ( i = *start_end; i < len; i++ ) { - if (string[i] == delimiter || i == len-1) { // We've reached the delimiter or the end. - break; - } - } - - /* Create and copy the substring what we need */ - output = strncpy(ecalloc(i - *start_end, sizeof(*output)), - string + *start_end, i - *start_end); - - output[i - *start_end] = '\0'; // null terminate - *start_end = i+1; - - return output; -} - -/* The `getline` from stdio.h wasn't working so I've made my own. */ -char* get_line(int fd) { - char *output, buffer[512], character; - int i = 0, r = i; - - while ((r = read(fd, &character, 1)) > 0 && i < 512) { - buffer[i] = character; - i++; - - if (character == '\n') - break; - } - - /* Checking for edge cases */ - if ((r == 0 && i == 0) || (i == 1 && buffer[i] == '\n')) - return NULL; - - buffer[i] = '\0'; - output = strcpy(ecalloc(i, sizeof(*output)), buffer); - - return output; -} - -void on_stdin(void) { - while (1) { - char *buffer = get_line(STDIN_FILENO); - if (!buffer || !strlen(buffer)) { - free(buffer); - return; - } - - handle_stdin(buffer); - free(buffer); - } -} - -void handle_stdin(char* line) { - char *name, *command; - Monitor* monitor; - ulong loc = 0; /* Keep track of where we are in the string `line` */ - - name = to_delimiter(line, &loc, ' '); - command = to_delimiter(line, &loc, ' '); - monitor = monitor_from_name(name); - if (!monitor) - return; - free(name); - - // Hate the way these if statements look. Is being this explicit worth it? - if (strcmp(command, "title") == EQUAL) { - if (line[strlen(line)-2] == ' ') { - bar_set_title(monitor->bar, ""); - goto done; - } - - char* title = to_delimiter(line, &loc, '\n'); - bar_set_title(monitor->bar, title); - free(title); - - } else if (strcmp(command, "appid") == EQUAL) { - /* Do nothing */ - } else if (strcmp(command, "floating") == EQUAL) { - if (line[strlen(line)-2] == ' ') { - bar_set_floating(monitor->bar, 0); - goto done; - } - - char* is_floating = to_delimiter(line, &loc, '\n'); - strcmp(is_floating, "1") == EQUAL ? bar_set_floating(monitor->bar, 1) : bar_set_floating(monitor->bar, 0); - free(is_floating); - - } else if (strcmp(command, "fullscreen") == EQUAL) { - /* Do nothing */ - } else if (strcmp(command, "selmon") == EQUAL) { - char* selmon = to_delimiter(line, &loc, '\n'); - strcmp(selmon, "1") == EQUAL ? bar_set_active(monitor->bar, 1) : bar_set_active(monitor->bar, 0); - free(selmon); - - } else if (strcmp(command, "tags") == EQUAL) { - char *occupied_, *tags__, *clients_, *urgent_; - int occupied, tags_, clients, urgent, i, tag_mask; - - occupied_ = to_delimiter(line, &loc, ' '); - tags__ = to_delimiter(line, &loc, ' '); - clients_ = to_delimiter(line, &loc, ' '); - urgent_ = to_delimiter(line, &loc, '\n'); - - occupied = atoi(occupied_); - tags_ = atoi(tags__); - clients = atoi(clients_); - urgent = atoi(urgent_); - - for(i = 0; i < LENGTH(tags); i++) { - int state = TAG_INACTIVE; - tag_mask = 1 << i; - - if (tags_ & tag_mask) - state |= TAG_ACTIVE; - if (urgent & tag_mask) - state |= TAG_URGENT; - - bar_set_tag(monitor->bar, i, state, occupied & tag_mask ? 1: 0, clients & tag_mask ? 1 : 0); - } - - free(occupied_); - free(tags__); - free(clients_); - free(urgent_); - - } else if (strcmp(command, "layout") == EQUAL) { - char* layout = to_delimiter(line, &loc, '\n'); - bar_set_layout(monitor->bar, layout); - free(layout); - } else { - /* TODO: Find a good way to tell the user a command wasn't recognized. Or just do nothing? */ - } - - done: - free(command); - update_monitor(monitor); -} - -void on_status(void) { - while (1) { - char *line = get_line(fifo_fd), *command, *status; - ulong loc = 0; - if (!line || !strlen(line)) { - free(line); - return; - } - - command = to_delimiter(line, &loc, ' '); - status = to_delimiter(line, &loc, '\n'); - - if (strcmp(command, "status") != EQUAL) { - free(status); - goto done; - } - - Monitor *current_monitor; - wl_list_for_each(current_monitor, &monitors, link) { - bar_set_status(current_monitor->bar, status); - update_monitor(current_monitor); - } - - - done: - free(line); - free(command); - free(status); - } -} - -void setup(void) { - if (pipe(self_pipe) < 0) - die("pipe"); - - set_cloexec(self_pipe[0]); - set_cloexec(self_pipe[1]); - - sighandle.sa_handler = &sighandler; - child_handle.sa_handler = SIG_IGN; - - if (sigaction(SIGTERM, &sighandle, NULL) < 0 || - sigaction(SIGINT, &sighandle, NULL) < 0 || - sigaction(SIGCHLD, &child_handle, NULL) < 0) - die("sigaction"); - - display = wl_display_connect(NULL); - if (!display) - die("Failed to connect to a Wayland display."); - wl_display_fd = wl_display_get_fd(display); - - wl_list_init(&seats); - wl_list_init(&monitors); - wl_list_init(&uninitializedOutputs); - - wl_registry *registry = wl_display_get_registry(display); - wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_roundtrip(display); - - setupFifo(); - - globalChecks(); /* Make sure we're ready to start the rest of the program */ - - wl_display_roundtrip(display); - - // Initalize any outputs we got from registry. Then free the outputs. - uninitOutput *output, *tmp; - wl_list_for_each_safe(output, tmp, &uninitializedOutputs, link) { - monitorSetup(output->name, output->output); - wl_list_remove(&output->link); - free(output); - } - - wl_display_roundtrip(display); - - pollfds = ecalloc(POLLFDS, sizeof(*pollfds)); - - pollfds[0] = (pollfd) {STDIN_FILENO, POLLIN}; - pollfds[1] = (pollfd) {wl_display_fd, POLLIN}; - pollfds[2] = (pollfd) {self_pipe[0], POLLIN}; - pollfds[3] = (pollfd) {fifo_fd, POLLIN}; - - if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) < 0) - die("O_NONBLOCK"); -} - -void run(void) { - struct pollfd *pollfd; - int i, quitting = 0; - - while (!quitting) { - flush(); - if (poll(pollfds, POLLFDS, -1) < 0 && errno != EINTR) - die("poll"); - - for (i = 0; i < POLLFDS; i++) { - pollfd = &pollfds[i]; - if (pollfd->revents & POLLNVAL) - die("POLLNVAL"); - - if (pollfd->fd == wl_display_fd) { - if ((pollfd->revents & POLLIN) && (wl_display_dispatch(display) < 0)) - die("wl_display_dispatch"); - - if (pollfd->revents & POLLOUT) { - pollfd->events = POLLIN; - flush(); - } - } else if (pollfd->fd == STDIN_FILENO && (pollfd->revents & POLLIN)) { - on_stdin(); - } else if (pollfd->fd == fifo_fd && (pollfd->revents & POLLIN)) { - on_status(); - } else if (pollfd->fd == self_pipe[0] && (pollfd->revents & POLLIN)) { - quitting = 1; - } - } - } +void check_globals(void) { + check_global(base, "xdg_wm_base"); + check_global(compositor, "wl_compositor"); + check_global(output_manager, "zxdg_output_manager_v1"); + check_global(shell, "zwlr_layer_shell_v1"); + check_global(shm, "wl_shm"); } void cleanup(void) { - if (shm) - wl_shm_destroy(shm); - if (compositor) - wl_compositor_destroy(compositor); - if (base) xdg_wm_base_destroy(base); - if (shell) - zwlr_layer_shell_v1_destroy(shell); - if (output_manager) - zxdg_output_manager_v1_destroy(output_manager); - if (fifo_path) { + wl_compositor_destroy(compositor); + close(fifo_fd); unlink(fifo_path); - remove(fifo_path); - } - if (display) + free(fifo_path); + zxdg_output_manager_v1_destroy(output_manager); + zwlr_layer_shell_v1_destroy(shell); + wl_shm_destroy(shm); + events_destroy(events); + log_destroy(); + + struct Monitor *monitor, *tmp_monitor; + wl_list_for_each_safe(monitor, tmp_monitor, &monitors, link) + monitor_destroy(monitor); + + struct Seat *seat, *tmp_seat; + wl_list_for_each_safe(seat, tmp_seat, &seats, link) + seat_destroy(seat); + wl_display_disconnect(display); } -int main(int argc, char *argv[]) { - int opt; // Just char for parsing args - while ((opt = getopt(argc, argv, "vh")) != -1) { - switch (opt) { - case 'h': - printf("Usage: %s [-h] [-v]\n", argv[0]); - printf(" -h: show this\n"); - printf(" -v: get version\n"); - exit(0); - - case 'v': - printf("dwl %f", VERSION); - exit(0); +void display_in(int fd, short mask, void *data) { + if (mask & (POLLHUP | POLLERR) || + wl_display_dispatch(display) == -1) { + running = 0; + return; } +} + +void fifo_handle(const char *line) { + char *command; + unsigned long loc = 0; + + command = to_delimiter(line, &loc, ' '); + + bar_log(LOG_INFO, "fifo_handle: command: '%s'", command); + + if (STRING_EQUAL(command, "status")) { + char *status = to_delimiter(line, &loc, '\n'); + bar_log(LOG_INFO, "fifo_handle: status: '%s'", status); + struct Monitor *pos; + wl_list_for_each(pos, &monitors, link) { + bar_set_status(pos->bar, status); + pipeline_invalidate(pos->pipeline); + } + free(status); + } + + free(command); +} + +void fifo_in(int fd, short mask, void *data) { + //bar_log(LOG_INFO, "fifo_in: start"); + + if (mask & POLLERR) { + bar_log(LOG_INFO, "fifo_in: POLLERR"); + events_remove(events, fd); + char *default_status = string_create("dwl %.1f", VERSION); + struct Monitor *pos; + wl_list_for_each(pos, &monitors, link) { + bar_set_status(pos->bar, default_status); + pipeline_invalidate(pos->pipeline); + } + free(default_status); + return; + } + + int new_fd = dup(fd); + FILE *fifo_file = fdopen(new_fd, "r"); + char *buffer = NULL; + size_t size = 0; + while (1) { + if (getline(&buffer, &size, fifo_file) == -1) { + //bar_log(LOG_INFO, "fifo_in: breaking"); + break; + } + + //bar_log(LOG_INFO, "fifo_in: '%s'", buffer); + fifo_handle(buffer); + } + free(buffer); + fclose(fifo_file); + close(new_fd); + + //bar_log(LOG_INFO, "fifo_in: done"); +} + +void fifo_setup(void) { + int result, i; + char *runtime_path = getenv("XDG_RUNTIME_DIR"); + + for (i = 0; i < 100; i++) { + fifo_path = string_create("%s/dwl-bar-%d", runtime_path, i); + + result = mkfifo(fifo_path, 0666); + if (result < 0) { + if (errno != EEXIST) + panic("mkfifo"); + + continue; + } + + if ((fifo_fd = open(fifo_path, O_CLOEXEC | O_RDONLY | O_NONBLOCK)) < 0) + panic("open fifo"); + + return; } - setup(); - run(); - cleanup(); - - return 0; + panic("setup fifo"); /* If we get here then we couldn't setup the fifo */ } -void die(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - fprintf(stderr, "[dwl-bar] error: "); - vfprintf(stderr, fmt, ap); - va_end(ap); +void monitor_destroy(struct Monitor *monitor) { + if (!monitor) + return; - if (fmt[0] && fmt[strlen(fmt) - 1] == ':') { - fputc(' ', stderr); - perror(NULL); - - } else { - fputc('\n', stderr); - } - fflush(stderr); - - cleanup(); - exit(1); + wl_list_remove(&monitor->link); + free(monitor->xdg_name); + wl_output_release(monitor->wl_output); + list_elements_destroy(monitor->hotspots, free); + pipeline_destroy(monitor->pipeline); + bar_destroy(monitor->bar); + free(monitor); } -void* ecalloc(size_t amnt, size_t size) { - void *p; +struct Monitor *monitor_from_name(const char *name) { + struct Monitor *pos; + wl_list_for_each(pos, &monitors, link) { + if (STRING_EQUAL(name, pos->xdg_name)) + return pos; + } - if (!(p = calloc(amnt, size))) - die("calloc did not allocate"); - - return p; + return NULL; +} + +struct Monitor *monitor_from_surface(const struct wl_surface *surface) { + struct Monitor *pos; + wl_list_for_each(pos, &monitors, link) { + if (pos->pipeline->surface == surface) + return pos; + } + + return NULL; +} + +void monitor_update(struct Monitor *monitor) { + if (!monitor) + return; + + if (!pipeline_is_visible(monitor->pipeline)) { + pipeline_show(monitor->pipeline, monitor->wl_output); + return; + } + + pipeline_invalidate(monitor->pipeline); +} + +void pipe_in(int fd, short mask, void *data) { + running = 0; +} + +void registry_global_add(void *data, struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) { + if (STRING_EQUAL(interface, wl_compositor_interface.name)) + compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4); + else if (STRING_EQUAL(interface, wl_output_interface.name)) { + struct Monitor *monitor = ecalloc(1, sizeof(*monitor)); + monitor->wl_output = wl_registry_bind(registry, name, &wl_output_interface, 1); + monitor->wl_name = name; + monitor->xdg_name = NULL; + monitor->xdg_output = NULL; + monitor->hotspots = list_create(1); + monitor->pipeline = pipeline_create(); + monitor->bar = bar_create(monitor->hotspots, monitor->pipeline); + + if (!monitor->pipeline || !monitor->bar) + return; + + wl_list_insert(&monitors, &monitor->link); + + if (!output_manager) + return; + + monitor->xdg_output = zxdg_output_manager_v1_get_xdg_output(output_manager, monitor->wl_output); + zxdg_output_v1_add_listener(monitor->xdg_output, &xdg_output_listener, monitor); + } + else if (STRING_EQUAL(interface, wl_seat_interface.name)) { + struct Seat *seat = ecalloc(1, sizeof(*seat)); + seat->seat = wl_registry_bind(registry, name, &wl_seat_interface, 5); + seat->wl_name = name; + seat->pointer = NULL; + seat->touch = NULL; + wl_list_insert(&seats, &seat->link); + wl_seat_add_listener(seat->seat, &seat_listener, seat); + } + else if (STRING_EQUAL(interface, wl_shm_interface.name)) + shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + else if (STRING_EQUAL(interface, xdg_wm_base_interface.name)) { + base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 2); + xdg_wm_base_add_listener(base, &xdg_wm_base_listener, NULL); + } + else if (STRING_EQUAL(interface, zxdg_output_manager_v1_interface.name)) { + output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, 3); + + struct Monitor *pos; + wl_list_for_each(pos, &monitors, link) { + // If the monitor is getting or has the xdg_name. + if (pos->xdg_output || pos->xdg_name) + continue; + + pos->xdg_output = zxdg_output_manager_v1_get_xdg_output(output_manager, pos->wl_output); + zxdg_output_v1_add_listener(pos->xdg_output, &xdg_output_listener, pos); + } + } + else if (STRING_EQUAL(interface, zwlr_layer_shell_v1_interface.name)) + shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 4); +} + +void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) { + struct Monitor *monitor, *tmp_monitor; + wl_list_for_each_safe(monitor, tmp_monitor, &monitors, link) { + if (monitor->wl_name != name) continue; + wl_list_remove(&monitor->link); + monitor_destroy(monitor); + } + + struct Seat *seat, *tmp_seat; + wl_list_for_each_safe(seat, tmp_seat, &seats, link) { + if (seat->wl_name != name) continue; + wl_list_remove(&seat->link); + seat_destroy(seat); + } +} + +void run(void) { + running = 1; + + while (running) { + wl_display_dispatch_pending(display); + if (wl_display_flush(display) == -1 && errno != EAGAIN) + break; + + events_poll(events); + } +} + +void set_cloexec(int fd) { + int flags = fcntl(fd, F_GETFD); + if (flags == -1) + panic("F_GETFD"); + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) + panic("FD_SETFD"); +} + +void setup(void) { + if (pipe(self_pipe) == -1) + panic("pipe"); + + set_cloexec(self_pipe[0]); + set_cloexec(self_pipe[1]); + + static struct sigaction sighandle; + static struct sigaction child_sigaction; + + sighandle.sa_handler = &sigaction_handler; + child_sigaction.sa_handler = SIG_IGN; + + if (sigaction(SIGTERM, &sighandle, NULL) < 0) + panic("sigaction SIGTERM"); + if (sigaction(SIGINT, &sighandle, NULL) < 0) + panic("sigaction SIGINT"); + if (sigaction(SIGCHLD, &child_sigaction, NULL) < 0) + panic("sigaction SIGCHLD"); + + display = wl_display_connect(NULL); + if (!display) + panic("Failed to connect to Wayland compositor."); + display_fd = wl_display_get_fd(display); + + wl_list_init(&seats); + wl_list_init(&monitors); + + struct wl_registry *registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener, NULL); + wl_display_roundtrip(display); + + fifo_setup(); + + check_globals(); + + wl_display_roundtrip(display); + + if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) < 0) + panic("STDIN_FILENO O_NONBLOCK"); + + events = events_create(); + events_add(events, display_fd, POLLIN, NULL, display_in); + events_add(events, self_pipe[0], POLLIN, NULL, pipe_in); + events_add(events, STDIN_FILENO, POLLIN, NULL, stdin_in); + events_add(events, fifo_fd, POLLIN, NULL, fifo_in); +} + +void stdin_handle(const char *line) { + if (!line) + return; + + char *name, *command; + struct Monitor *monitor; + unsigned long loc = 0; /* Keep track of where we are in the string `line` */ + + name = to_delimiter(line, &loc, ' '); + command = to_delimiter(line, &loc, ' '); + monitor = monitor_from_name(name); + if (!monitor) { + free(name); + free(command); + return; + } + free(name); + + if (STRING_EQUAL(command, "title")) { + char *title = to_delimiter(line, &loc, '\n'); + if (*title == '\0') { + bar_set_title(monitor->bar, ""); + } else + bar_set_title(monitor->bar, title); + free(title); + } else if (STRING_EQUAL(command, "appid")) { + /* Do nothing */ + } else if (STRING_EQUAL(command, "floating")) { + char *is_floating = to_delimiter(line, &loc, '\n'); + if (*is_floating == '1') + bar_set_floating(monitor->bar, 1); + else + bar_set_floating(monitor->bar, 0); + free(is_floating); + } else if (STRING_EQUAL(command, "fullscreen")) { + /* Do nothing */ + } else if (STRING_EQUAL(command, "selmon")) { + char *selmon = to_delimiter(line, &loc, '\n'); + if (*selmon == '1') + bar_set_active(monitor->bar, 1); + else + bar_set_active(monitor->bar, 0); + free(selmon); + } else if (STRING_EQUAL(command, "tags")) { + char *occupied_str, *tags_str, *clients_str, *urgent_str; + int occupied, _tags, clients, urgent, i, tag_mask, state; + + occupied_str = to_delimiter(line, &loc, ' '); + tags_str = to_delimiter(line, &loc, ' '); + clients_str = to_delimiter(line, &loc, ' '); + urgent_str = to_delimiter(line, &loc, ' '); + + occupied = atoi(occupied_str); + _tags = atoi(tags_str); + clients = atoi(clients_str); + urgent = atoi(urgent_str); + + for (i = 0; i < LENGTH(tags); i++) { + state = Tag_None; + tag_mask = 1 << i; + + if (_tags & tag_mask) + state |= Tag_Active; + if (urgent & tag_mask) + state |= Tag_Urgent; + + bar_set_tag(monitor->bar, i, state, occupied & tag_mask ? 1 : 0, clients & tag_mask ? 1 : 0); + } + + free(occupied_str); + free(tags_str); + free(clients_str); + free(urgent_str); + } else if (STRING_EQUAL(command, "layout")) { + char *layout = to_delimiter(line, &loc, '\n'); + bar_set_layout(monitor->bar, layout); + free(layout); + } + + free(command); + monitor_update(monitor); +} + +void stdin_in(int fd, short mask, void *data) { + if (mask & (POLLHUP | POLLERR)) { + running = 0; + return; + } + + int new_fd = dup(fd); + FILE *stdin_file = fdopen(new_fd, "r"); + char *buffer = NULL; + size_t size = 0; + while(1) { + if (getline(&buffer, &size, stdin_file) == -1) + break; + + stdin_handle(buffer); + } + free(buffer); + fclose(stdin_file); + close(new_fd); +} + +void sigaction_handler(int _) { + if (write(self_pipe[1], "0", 1) < 0) + panic("sigaction_handler"); +} + +void xdg_output_name(void *data, struct zxdg_output_v1 *output, const char *name) { + struct Monitor *monitor = data; + monitor->xdg_name = strdup(name); + zxdg_output_v1_destroy(output); + monitor->xdg_output = NULL; +} + +void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial) { + xdg_wm_base_pong(xdg_wm_base, serial); +} + +int main(int argc, char *argv[]) { + int opt; + while((opt = getopt(argc, argv, "l")) != -1) { + switch (opt) { + case 'l': + if (!setup_log()) + panic("Failed to setup logging"); + break; + case 'h': + printf("Usage: %s [-h] [-v]\n", argv[0]); + exit(EXIT_SUCCESS); + case 'v': + printf("%s %.1f\n", argv[0], VERSION); + exit(EXIT_SUCCESS); + case '?': + printf("Invalid Argument\n"); + printf("Usage: %s [-h] [-v] [-l]\n", argv[0]); + exit(EXIT_FAILURE); + } + } + + setup(); + run(); + cleanup(); +} + +void panic(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "[dwl-bar] panic: "); + vfprintf(stderr, fmt, ap); + va_end(ap); + if (fmt[0] && fmt[strlen(fmt) - 1] == ':') { + fputc(' ', stderr); + perror(NULL); + + } else { + fputc('\n', stderr); + } + + cleanup(); + exit(EXIT_FAILURE); } diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..175a5e1 --- /dev/null +++ b/src/main.h @@ -0,0 +1,31 @@ +#ifndef MAIN_H_ +#define MAIN_H_ + +#include "xdg-output-unstable-v1-protocol.h" +#include +#include +#include + +#define VERSION 0.0 + +struct Monitor { + char *xdg_name; + uint32_t wl_name; + + struct wl_output *wl_output; + struct zxdg_output_v1 *xdg_output; + struct Pipeline *pipeline; + struct List *hotspots; /* struct Hotspot* */ + struct Bar *bar; + + struct wl_list link; +}; + +void panic(const char *fmt, ...); +struct Monitor *monitor_from_surface(const struct wl_surface *surface); + +extern struct wl_compositor *compositor; +extern struct zwlr_layer_shell_v1 *shell; +extern struct wl_shm *shm; + +#endif // MAIN_H_ diff --git a/src/render.c b/src/render.c new file mode 100644 index 0000000..9e3308b --- /dev/null +++ b/src/render.c @@ -0,0 +1,259 @@ +#include "render.h" +#include "log.h" +#include "main.h" +#include "shm.h" +#include "util.h" +#include "config.h" +#include "wlr-layer-shell-unstable-v1-protocol.h" +#include "pango/pango-layout.h" +#include "pango/pangocairo.h" +#include +#include +#include +#include + +static struct Font *get_font(void); +static void pipeline_frame(void* data, struct wl_callback* callback, uint32_t callback_data); +static void pipeline_layer_surface(void* data, struct zwlr_layer_surface_v1* _, uint32_t serial, uint32_t width, uint32_t height); +static void pipeline_render(struct Pipeline *pipeline); + +const struct wl_callback_listener frame_listener = {.done = pipeline_frame}; +const struct zwlr_layer_surface_v1_listener layer_surface_listener = {.configure = pipeline_layer_surface}; + +struct BasicComponent *basic_component_create(PangoContext *context, PangoFontDescription *description) { + struct BasicComponent *component = ecalloc(1, sizeof(*component)); + component->layout = pango_layout_new(context); + pango_layout_set_font_description(component->layout, description); + component->x = 0; + component->y = 0; + component->width = 0; + component->height = 0; + component->tx = 0; + component->ty = 0; + + return component; +} + +void basic_component_destroy(struct BasicComponent *component) { + if (!component) + return; + + g_object_unref(component->layout); + free(component); +} + +int basic_component_is_clicked(struct BasicComponent *component, double x, double y) { + bar_log(LOG_INFO, "basic_component_is_clicked: %.1f x %.1f in %d x %d", x, y, component->x, component->x+component->width); + return (x > component->x && y > component->y && + x < (component->x + component->width) && y < (component->y + component->height)); +} + +void basic_component_render(struct BasicComponent *component, struct Pipeline *pipeline, + cairo_t *painter, int *x, int *y) { + if (!component) + return; + + pango_cairo_update_layout(painter, component->layout); + component->x = *x; + component->y = *y; + + pipeline_color_background(pipeline, painter); + cairo_rectangle(painter, *x, *y, component->width, component->height); + cairo_fill(painter); + + pipeline_color_foreground(pipeline, painter); + cairo_move_to(painter, *x+component->tx, *y+component->ty); + pango_cairo_show_layout(painter, component->layout); +} + +int basic_component_text_width(struct BasicComponent *component) { + if (!component) + return 0; + + int w; + pango_layout_get_size(component->layout, &w, NULL); + return PANGO_PIXELS(w); +} + +struct Font *get_font(void) { + PangoFontMap* map = pango_cairo_font_map_get_default(); + if (!map) + panic("font map"); + + PangoFontDescription* desc = pango_font_description_from_string(font); + if (!desc) + panic("font description"); + + PangoContext* context = pango_font_map_create_context(map); + if (!context) + panic("temp context"); + + PangoFont* fnt = pango_font_map_load_font(map, context, desc); + if (!fnt) + panic("font load"); + + PangoFontMetrics* metrics = pango_font_get_metrics(fnt, pango_language_get_default()); + if (!metrics) + panic("font metrics"); + + struct Font *font = ecalloc(1, sizeof(*font)); + font->description = desc; + font->height = PANGO_PIXELS(pango_font_metrics_get_height(metrics)); + font->approx_width = PANGO_PIXELS(pango_font_metrics_get_approximate_char_width(metrics)); + + pango_font_metrics_unref(metrics); + g_object_unref(fnt); + g_object_unref(context); + + return font; +} + +void pipeline_add(struct Pipeline *pipeline, const struct PipelineListener *listener, void *data) { + if (!pipeline) + return; + + struct PipelineCallback *callback = list_add(pipeline->callbacks, ecalloc(1, sizeof(*callback))); + callback->listener = listener; + callback->data = data; +} + +struct Pipeline *pipeline_create(void) { + struct Pipeline *pipeline = ecalloc(1, sizeof(*pipeline)); + pipeline->callbacks = list_create(0); + pipeline->current = 0; + pipeline->invalid = 0; + pipeline->context = pango_font_map_create_context(pango_cairo_font_map_get_default()); + pipeline->font = get_font(); + pipeline->shm = NULL; + + return pipeline; +} + +void pipeline_destroy(struct Pipeline *pipeline) { + if (!pipeline) + return; + + list_elements_destroy(pipeline->callbacks, free); + g_object_unref(pipeline->context); + g_object_unref(pipeline->font->description); + free(pipeline->font); + shm_destroy(pipeline->shm); + wl_surface_destroy(pipeline->surface); + zwlr_layer_surface_v1_destroy(pipeline->layer_surface); + free(pipeline); +} + +void pipeline_frame(void* data, struct wl_callback* callback, uint32_t callback_data) { + pipeline_render((struct Pipeline *)data); + wl_callback_destroy(callback); +} + +int pipeline_get_future_widths(struct Pipeline *pipeline) { + if (!pipeline) + return 0; + + int width = 0; + struct PipelineCallback *callback; + for (int i = pipeline->callbacks->length-1; i > pipeline->current; i--) { + callback = pipeline->callbacks->data[i]; + width += callback->listener->width(pipeline, callback->data, width); + } + + return width; +} + +void pipeline_invalidate(struct Pipeline *pipeline) { + if (!pipeline || pipeline->invalid || !pipeline_is_visible(pipeline)) + return; + + struct wl_callback *callback = wl_surface_frame(pipeline->surface); + wl_callback_add_listener(callback, &frame_listener, pipeline); + wl_surface_commit(pipeline->surface); + pipeline->invalid = 1; +} + +int pipeline_is_visible(struct Pipeline *pipeline) { + if (!pipeline) return 0; + return !(!pipeline->surface); +} + +void pipeline_layer_surface(void* data, struct zwlr_layer_surface_v1* _, + uint32_t serial, uint32_t width, uint32_t height) { + struct Pipeline *pipeline = data; + zwlr_layer_surface_v1_ack_configure(pipeline->layer_surface, serial); + + if (pipeline->shm) { + if (pipeline->shm->width == width && pipeline->shm->height == height) + return; + shm_destroy(pipeline->shm); + } + + pipeline->shm = shm_create(width, height, WL_SHM_FORMAT_XRGB8888); + pipeline_render(pipeline); +} + +void pipeline_render(struct Pipeline *pipeline) { + if (!pipeline || !pipeline->shm) + return; + + int x = 0, y = 0; + cairo_surface_t *image = cairo_image_surface_create_for_data(shm_data(pipeline->shm), + CAIRO_FORMAT_ARGB32, pipeline->shm->width, pipeline->shm->height, pipeline->shm->stride); + cairo_t *painter = cairo_create(image); + pango_cairo_update_context(painter, pipeline->context); + + struct PipelineCallback *callback; + for (int i = 0; i < pipeline->callbacks->length; i++) { + pipeline->current = i; + callback = pipeline->callbacks->data[i]; + callback->listener->render(pipeline, callback->data, painter, &x, &y); + } + + wl_surface_attach(pipeline->surface, shm_buffer(pipeline->shm), 0, 0); + wl_surface_damage(pipeline->surface, 0, 0, pipeline->shm->width, pipeline->shm->height); + wl_surface_commit(pipeline->surface); + + cairo_destroy(painter); + cairo_surface_destroy(image); + + shm_flip(pipeline->shm); + pipeline->invalid = 0; +} + +void pipeline_show(struct Pipeline *pipeline, struct wl_output *output) { + if (!pipeline || !output || pipeline_is_visible(pipeline)) + return; + + pipeline->surface = wl_compositor_create_surface(compositor); + pipeline->layer_surface = zwlr_layer_shell_v1_get_layer_surface(shell, pipeline->surface, output, + ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "doom.dwl-bar"); + zwlr_layer_surface_v1_add_listener(pipeline->layer_surface, &layer_surface_listener, pipeline); + + int anchor = bar_top ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + zwlr_layer_surface_v1_set_anchor(pipeline->layer_surface, + anchor | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); + + int height = pipeline->font->height + 2; + zwlr_layer_surface_v1_set_size(pipeline->layer_surface, 0, height); + zwlr_layer_surface_v1_set_exclusive_zone(pipeline->layer_surface, height); + wl_surface_commit(pipeline->surface); +} + +void pipeline_set_colorscheme(struct Pipeline* pipeline, const int **scheme) { + for (int i = 0; i < 4; i++) { + pipeline->foreground[i] = scheme[0][i]; + pipeline->background[i] = scheme[1][i]; + } +} + +void pipeline_color_foreground(struct Pipeline* pipeline, cairo_t *painter) { + set_color(painter, pipeline->foreground); +} + +void pipeline_color_background(struct Pipeline* pipeline, cairo_t *painter) { + set_color(painter, pipeline->background); +} + +void set_color(cairo_t *painter, const int rgba[4]) { + cairo_set_source_rgba(painter, rgba[0]/255.0, rgba[1]/255.0, rgba[2]/255.0, rgba[3]/255.0); +} diff --git a/src/render.h b/src/render.h new file mode 100644 index 0000000..d60ad47 --- /dev/null +++ b/src/render.h @@ -0,0 +1,70 @@ +#ifndef RENDER_H_ +#define RENDER_H_ + +#include "util.h" +#include "shm.h" +#include "user.h" +#include "wlr-layer-shell-unstable-v1-protocol.h" +#include "pango/pango-types.h" +#include +#include + +struct Font { + PangoFontDescription *description; + unsigned int height, approx_width; +}; + +/* The render pipeline, also handles click events by keeping track of each components bounds'. */ +struct Pipeline { + struct List *callbacks; /* struct PipelineCallbacks* */ + int current /* The current callback we are on */, + invalid; + + /* Colors */ + int background[4], foreground[4]; + + PangoContext *context; + struct Font *font; + + struct Shm *shm; + struct wl_surface *surface; + struct zwlr_layer_surface_v1 *layer_surface; +}; + +struct PipelineListener { + void (*render)(struct Pipeline *pipeline, void *data, cairo_t *painter, int *x, int *y); + int (*width)(struct Pipeline *pipeline, void *data, unsigned int future_widths); +}; + +struct PipelineCallback { + const struct PipelineListener *listener; + void *data; +}; + +/* Basic helper component, can be used if the only thing to be displayed is text. */ +struct BasicComponent { + PangoLayout *layout; + int width, height, + x, y /* box start coordinates */, + tx, ty /* text starts coordinates, tx is added to starting x coordinates. */; +}; + +struct BasicComponent *basic_component_create(PangoContext *context, PangoFontDescription *description); +void basic_component_destroy(struct BasicComponent *component); +int basic_component_is_clicked(struct BasicComponent *component, double x, double y); +void basic_component_render(struct BasicComponent *component, struct Pipeline *pipeline, + cairo_t *painter, int *x, int *y); +int basic_component_text_width(struct BasicComponent *component); +void pipeline_add(struct Pipeline *pipeline, const struct PipelineListener *listener, void *data); +struct Pipeline *pipeline_create(void); +void pipeline_destroy(struct Pipeline *pipeline); +int pipeline_get_future_widths(struct Pipeline *pipeline); +void pipeline_invalidate(struct Pipeline *pipeline); +int pipeline_is_visible(struct Pipeline *pipeline); +void pipeline_show(struct Pipeline *pipeline, struct wl_output *output); +void pipeline_set_colorscheme(struct Pipeline* pipeline, const int **scheme); +void pipeline_color_foreground(struct Pipeline* pipeline, cairo_t *painter); +void pipeline_color_background(struct Pipeline* pipeline, cairo_t *painter); +void set_color(cairo_t *painter, const int rgba[4]); + +#endif // RENDER_H_ diff --git a/src/shm.c b/src/shm.c index e4365b6..896c827 100644 --- a/src/shm.c +++ b/src/shm.c @@ -1,148 +1,121 @@ -#include -#include -#include -#include -#include -#include -#include +#include "shm.h" +#include "main.h" #include -#include "common.h" -#include "shm.h" - -/* For unmapping the mapped memory when we're finished with the shared memory. */ -typedef struct MemoryMapping { - void* ptr; - int size; -} MemoryMapping; - -/* To help us keep track of our wayland buffers and their data. */ -typedef struct Buffer { - wl_buffer* buffer; - uint8_t* buffer_ptr; -} Buffer; - -struct ShmPriv { - MemoryMapping* memory; - Buffer buffers[2]; - int current; // Since we hold multiple buffers we need to know which we're currently using. -}; - -static MemoryMapping* memory_mapping_create(int fd, int pool_size); -void memory_mapping_destroy(MemoryMapping* map); - -static Buffer buffer_create(MemoryMapping* memmap, wl_shm_pool* shm, int fd, int width, int height, int offset, wl_shm_format format); -static void buffer_destroy(Buffer* buf); +#define BUFFERS 2 static int allocate_shm(int size); +static struct Buffer buffer_create(struct MemoryMapping *memmap, struct wl_shm_pool *shm, + int fd, int width, int height, int offset, enum wl_shm_format format); +static void buffer_destroy(struct Buffer *buf); +static struct MemoryMapping memory_mapping_create(int fd, int pool_size); +static void memory_mapping_destroy(struct MemoryMapping *map); + -static const int buffer_amnt = 2; int allocate_shm(int size) { char name[] = "wl_shm"; int fd; - if ((fd = shm_open(name, O_CREAT | O_RDWR | O_EXCL, 0600)) < 0) { - die("shm_open when allocating shm"); - } + if ((fd = shm_open(name, O_CREAT | O_RDWR | O_EXCL, 0600)) < 0) + panic("shm_open when allocating shm"); + shm_unlink(name); - if (ftruncate(fd, size) < 0) { - die("ftruncate when allocating shm"); - } + + if (ftruncate(fd, size) < 0) + panic("ftruncate when allocating shm"); return fd; } -MemoryMapping* memory_mapping_create(int fd, int pool_size) { - MemoryMapping* map = ecalloc(1, sizeof(MemoryMapping)); - void* ptr = mmap(NULL, pool_size, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, 0); - if (ptr == MAP_FAILED || !ptr) { - close(fd); - die("MAP_FAILED"); - } +static struct Buffer buffer_create(struct MemoryMapping *map, struct wl_shm_pool *shm, + int fd, int width, int height, int offset, enum wl_shm_format format) { + if (!map) + panic("map is null"); - map->ptr = ptr; - map->size = pool_size; - - return map; -} - -void memory_mapping_destroy(MemoryMapping* map) { - munmap(map->ptr, map->size); - free(map); -} - -Buffer buffer_create(MemoryMapping* memmap, wl_shm_pool* pool, int fd, int width, int height, int offset, wl_shm_format format) { int stride = width * 4, pool_size = height * stride; - Buffer buffer; + struct Buffer buffer; - if (!memmap) - die("memmap is null"); - - wl_buffer* wl_buf = wl_shm_pool_create_buffer(pool, offset, width, height, stride, format); - - buffer.buffer = wl_buf; - buffer.buffer_ptr = memmap->ptr+offset; + struct wl_buffer *wl_buffer = wl_shm_pool_create_buffer(shm, offset, width, height, stride, format); + buffer.buffer = wl_buffer; + buffer.buffer_ptr = map->ptr+offset; return buffer; } -void buffer_destroy(Buffer* buffer) { +void buffer_destroy(struct Buffer *buffer) { + if (!buffer) return; + wl_buffer_destroy(buffer->buffer); + free(buffer); } -Shm* shm_create(int w, int h, wl_shm_format format) { - Shm* _shm = calloc(1, sizeof(Shm)); - ShmPriv* priv = calloc(1, sizeof(ShmPriv)); - MemoryMapping* memory; +struct MemoryMapping memory_mapping_create(int fd, int pool_size) { + struct MemoryMapping map; + void* ptr = mmap(NULL, pool_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED || !ptr) { + close(fd); + panic("MAP_FAILED"); + } + + map.ptr = ptr; + map.size = pool_size; + + return map; +} + +void memory_mapping_destroy(struct MemoryMapping *map) { + if (!map) return; + + munmap(map->ptr, map->size); + free(map); +} + +struct Shm *shm_create(int width, int height, enum wl_shm_format format) { + struct Shm *shared_mem = calloc(1, sizeof(*shared_mem)); int i, offset, - stride = w * 4, - size = stride * h, - total = size * buffer_amnt; + stride = width * 4, + size = stride * height, + total = size * BUFFERS; int fd = allocate_shm(total); - memory = memory_mapping_create(fd, total); - wl_shm_pool* pool = wl_shm_create_pool(shm, fd, total); - for (i = 0; i < buffer_amnt; i++) { + struct MemoryMapping memory = memory_mapping_create(fd, total); + struct wl_shm_pool* pool = wl_shm_create_pool(shm, fd, total); + for (i = 0; i < BUFFERS; i++) { offset = size*i; - priv->buffers[i] = buffer_create(memory, pool, fd, w, h, offset, format); + shared_mem->buffers[i] = buffer_create(&memory, pool, fd, width, height, offset, format); } close(fd); wl_shm_pool_destroy(pool); - priv->memory = memory; - priv->current = 0; + shared_mem->map = memory; + shared_mem->current = 0; - _shm->priv = priv; - _shm->height = h; - _shm->width = w; - _shm->stride = stride; + shared_mem->height = height; + shared_mem->width = width; + shared_mem->stride = stride; - return _shm; + return shared_mem; } -void shm_destroy(Shm* shm) { - uint i; - if (shm->priv->memory) { - memory_mapping_destroy(shm->priv->memory); - } +void shm_destroy(struct Shm *shm) { + if (!shm) return; - for (i = 0; i < buffer_amnt; i++) { // REVIEW this may cause problems - if (&shm->priv->buffers[i]) { - buffer_destroy(&shm->priv->buffers[i]); - } - } + memory_mapping_destroy(&shm->map); + for (int i = 0; i < BUFFERS; i++) + buffer_destroy(&shm->buffers[i]); + free(shm); } -uint8_t* shm_data(Shm *shm) { - return shm->priv->buffers[shm->priv->current].buffer_ptr; +uint8_t *shm_data(struct Shm *shm) { + return shm->buffers[shm->current].buffer_ptr; } -wl_buffer* shm_buffer(Shm *shm) { - return shm->priv->buffers[shm->priv->current].buffer; +struct wl_buffer *shm_buffer(struct Shm *shm) { + return shm->buffers[shm->current].buffer; } -void shm_flip(Shm *shm) { - shm->priv->current = 1-shm->priv->current; +void shm_flip(struct Shm *shm) { + shm->current = 1-shm->current; } diff --git a/src/shm.h b/src/shm.h index f1d3d64..b736336 100644 --- a/src/shm.h +++ b/src/shm.h @@ -1,24 +1,35 @@ #ifndef SHM_H_ #define SHM_H_ -#include "common.h" + #include +#include +#include +#include +#include +#include #include -typedef struct ShmPriv ShmPriv; +struct MemoryMapping { + void *ptr; + int size; +}; + +struct Buffer { + struct wl_buffer *buffer; + uint8_t *buffer_ptr; +}; struct Shm { - int width; - int height; - int stride; - - ShmPriv* priv; + int width, height, stride, current; + struct MemoryMapping map; + struct Buffer buffers[2]; }; -typedef struct Shm Shm; -Shm* shm_create(int w, int h, wl_shm_format format); -void shm_destroy(Shm* shm); -uint8_t* shm_data(Shm* shm); -wl_buffer* shm_buffer(Shm* shm); -void shm_flip(Shm* shm); +struct Shm *shm_create(int width, int height, enum wl_shm_format format); +void shm_destroy(struct Shm *shm); +uint8_t *shm_data(struct Shm *shm); +struct wl_buffer *shm_buffer(struct Shm *shm); +void shm_flip(struct Shm *shm); + #endif // SHM_H_ diff --git a/src/user.c b/src/user.c new file mode 100644 index 0000000..13820e7 --- /dev/null +++ b/src/user.c @@ -0,0 +1,17 @@ +#include "user.h" +#include "util.h" +#include +#include +#include + +void spawn(struct Monitor *monitor, const union Arg *arg) { + if (fork() != 0) + return; + + char* const* argv = arg->v; + setsid(); + execvp(argv[0], argv); + fprintf(stderr, "dwl-bar: execvp %s", argv[0]); + perror(" failed\n"); + exit(1); +} diff --git a/src/user.h b/src/user.h new file mode 100644 index 0000000..b4442d8 --- /dev/null +++ b/src/user.h @@ -0,0 +1,51 @@ +#ifndef USER_H_ +#define USER_H_ + +#include "main.h" + +typedef struct Binding Binding; + +enum Clicked { + Click_None, + Click_Tag, + Click_Layout, + Click_Title, + Click_Status, +}; + +enum ColorScheme { + InActive_Scheme = 0, + Active_Scheme = 1, + Urgent_Scheme = 2, +}; + +enum TouchGesture { + Gesture_Left, + Gesture_Right, +}; + +enum PointerScroll { + Scroll_Up, + Scroll_Down, + Scroll_Left, + Scroll_Right, +}; + +union Arg { + unsigned int ui; + int i; + const void *v; +}; + +struct Binding { + enum Clicked clicked; + int button; + void (*callback)(struct Monitor *monitor, const union Arg *arg); + /* Informs the click function that they should only pass the defined arg in this binding */ + unsigned int bypass; + const union Arg arg; +}; + +void spawn(struct Monitor *monitor, const union Arg *arg); + +#endif // USER_H_ diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..a2d9c89 --- /dev/null +++ b/src/util.c @@ -0,0 +1,141 @@ +#include "util.h" +#include "log.h" +#include "main.h" +#include +#include +#include +#include +#include + +static void list_resize(struct List *list); + +void *ecalloc(size_t amnt, size_t size) { + void *p = calloc(amnt, size); + + if (!p) + panic("Out of memory"); + + return p; +} + +void *list_add(struct List *list, void *data) { + if (!list) + return NULL; + + list_resize(list); + list->data[list->length++] = data; + return list->data[list->length-1]; +} + +void list_copy(struct List *dest, struct List *src) { + if (!dest || !src) + return; + + for (int i = 0; i < src->length; i++) + list_add(dest, src->data[i]); +} + +struct List *list_create(size_t initial_size) { + struct List *list = ecalloc(1, sizeof(*list)); + + list->allocated = initial_size > 10 ? initial_size : 10; + list->length = 0; + list->data = ecalloc(list->allocated, sizeof(void*)); + + return list; +} + +int list_cmp_find(struct List* list, const void *data, int compare(const void *left, const void *right)) { + if (!list) + return -1; + + for (int i = 0; i < list->length; i++) + if (compare(data, list->data[i])) + return i; + + return -1; +} + +void list_destroy(struct List *list) { + if (!list) + return; + + free(list->data); + free(list); +} + +void list_elements_destroy(struct List *list, void (*destroy)(void *data)) { + if (!list) + return; + + for (int i = 0; i < list->length; i++) + destroy(list->data[i]); + free(list->data); + free(list); +} + +int list_find(struct List* list, const void *data) { + if (!list) + return -1; + + for (int i = 0; i < list->length; i++) + if (data == list->data[i]) + return i; + + return -1; +} + +void *list_remove(struct List *list, unsigned int index) { + if (!list || index > list->length-1) + return NULL; + + void *tmp = list->data[index]; + + list->length--; + memmove(&list->data[index], &list->data[index + 1], sizeof(void*) * (list->length - index)); + + return tmp; +} + +void list_resize(struct List *list) { + if (list->length < list->allocated) + return; + + list->allocated *= 2; + list->data = realloc(list->data, list->allocated); +} + +char *string_create(const char *fmt, ...) { + va_list ap, aq; + va_start(ap, fmt); + va_copy(aq, ap); + + char *str; + size_t len = vsnprintf(NULL, 0, fmt, ap) + 1; + str = ecalloc(1, len); + vsnprintf(str, len, fmt, aq); + + va_end(ap); + va_end(aq); + + return str; +} + +char *to_delimiter(const char *string, unsigned long *start_end, char delimiter) { + if (!string || !start_end) + return NULL; + + char *output; + const char *read; + unsigned long i = 0; + + for (read = string + *start_end; *read != '\0' && *read != delimiter; read++) + i++; + + output = strncpy(ecalloc(i+1, sizeof(*output)), + string + *start_end, i); + output[i++] = '\0'; + *start_end += i; + + return output; +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..6d3534a --- /dev/null +++ b/src/util.h @@ -0,0 +1,29 @@ +#ifndef UTIL_H_ +#define UTIL_H_ +#include +#include +#include +#include + +#define STRING_EQUAL(string1, string2) strcmp(string1, string2) == 0 +#define STRINGN_EQUAL(string1, string2, n) strcmp(string1, string2, n) == 0 +#define LENGTH(X) (sizeof X / sizeof X[0] ) + +struct List { + void **data; + size_t allocated, length; +}; + +void *ecalloc(size_t amnt, size_t size); +void *list_add(struct List *list, void *data); +void list_copy(struct List *dest, struct List *src); +struct List *list_create(size_t initial_size); +int list_cmp_find(struct List *list, const void *data, int compare(const void *left, const void *right)); +void list_destroy(struct List *list); +void list_elements_destroy(struct List *list, void (*destroy)(void *data)); +int list_find(struct List* list, const void *data); +void *list_remove(struct List *list, unsigned int index); +char *string_create(const char* fmt, ...); +char *to_delimiter(const char* string, ulong *start_end, char delimiter); + +#endif // UTIL_H_