diff --git a/Makefile b/Makefile index b59e213912acc071d50d498b86d22756e8659c05..530473cc3a49041e2cf39b44af8124fb7c82d233 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,17 @@ VERSION=1.1 ARCHIVE=osdd-$(VERSION).tar.gz -CFLAGS=-g3 -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wredundant-decls -std=gnu99 +CFLAGS=-g3 -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wredundant-decls -std=gnu99 -fsanitize=address all: osdd osdc osd-batt osd-alsa -osdd: osdd.o util.o display.o +osdd: osdd.o util.o display.o osdd-set.o osdc: osdc.o util.o client.o osd-batt: osd-batt.o util.o client.o osd-alsa: osd-alsa.o util.o client.o -osdd: LDLIBS+=$(shell pkg-config --libs xft) -lXext -lX11 +osdd: LDLIBS+=$(shell pkg-config --libs xft) -lXext -lX11 +LDLIBS += -fsanitize=address osdc: LDLIBS+=-lX11 osd-batt: LDLIBS+=-lX11 diff --git a/display.c b/display.c index 08a680b84bf695b4417b87f5efc46ae571c3ec6c..ed8c57c3029d1f4d341ff9d49823e000064e8e96 100644 --- a/display.c +++ b/display.c @@ -19,13 +19,18 @@ #include <X11/extensions/render.h> #include <X11/Xft/Xft.h> -#undef DEBUG -#include "util.h" +#define DEBUG #include "display.h" +#include "util.h" #define SLIDERS_WITH_BRACKETS -struct osd_state { +static char *font_name = "times-64:bold"; +static double line_spacing = 0.2; + + + +struct display_state { // Basic characteristics of current display and screen Display *dpy; @@ -54,7 +59,7 @@ struct osd_state { XftDraw *image_draw; // Contents of the display - struct osd_line *lines; + struct display_line *lines; int num_lines; int max_lines; int line_distance; @@ -68,7 +73,7 @@ struct osd_state { }; static void -stay_on_top(struct osd_state *osd) +stay_on_top(struct display_state *display) { int format; unsigned long nitems, bytes_after; @@ -76,8 +81,8 @@ stay_on_top(struct osd_state *osd) Atom type; // Gnome-compliant way - Atom gnome = XInternAtom(osd->dpy, "_WIN_SUPPORTING_WM_CHECK", False); - if (XGetWindowProperty(osd->dpy, osd->root, gnome, 0, 16384, False, AnyPropertyType, + Atom gnome = XInternAtom(display->dpy, "_WIN_SUPPORTING_WM_CHECK", False); + if (XGetWindowProperty(display->dpy, display->root, gnome, 0, 16384, False, AnyPropertyType, &type, &format, &nitems, &bytes_after, &prop) == Success && nitems > 0) { @@ -86,18 +91,18 @@ stay_on_top(struct osd_state *osd) XClientMessageEvent e; memset(&e, 0, sizeof(e)); e.type = ClientMessage; - e.window = osd->win; - e.message_type = XInternAtom(osd->dpy, "_WIN_LAYER", False); + e.window = display->win; + e.message_type = XInternAtom(display->dpy, "_WIN_LAYER", False); e.format = 32; e.data.l[0] = 6; // WIN_LAYER_ONTOP */ - XSendEvent(osd->dpy, osd->root, False, SubstructureNotifyMask, (XEvent *) &e); + XSendEvent(display->dpy, display->root, False, SubstructureNotifyMask, (XEvent *) &e); XFree(prop); return; } // NetWM-compliant way - Atom net_wm = XInternAtom(osd->dpy, "_NET_SUPPORTED", False); - if (XGetWindowProperty(osd->dpy, osd->root, net_wm, 0, 16384, False, AnyPropertyType, + Atom net_wm = XInternAtom(display->dpy, "_NET_SUPPORTED", False); + if (XGetWindowProperty(display->dpy, display->root, net_wm, 0, 16384, False, AnyPropertyType, &type, &format, &nitems, &bytes_after, &prop) == Success && nitems > 0) { @@ -105,13 +110,13 @@ stay_on_top(struct osd_state *osd) XEvent e; memset(&e, 0, sizeof(e)); e.xclient.type = ClientMessage; - e.xclient.message_type = XInternAtom(osd->dpy, "_NET_WM_STATE", False); - e.xclient.display = osd->dpy; - e.xclient.window = osd->win; + e.xclient.message_type = XInternAtom(display->dpy, "_NET_WM_STATE", False); + e.xclient.display = display->dpy; + e.xclient.window = display->win; e.xclient.format = 32; e.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD - e.xclient.data.l[1] = XInternAtom(osd->dpy, "_NET_WM_STATE_STAYS_ON_TOP", False); - XSendEvent(osd->dpy, osd->root, False, SubstructureRedirectMask, &e); + e.xclient.data.l[1] = XInternAtom(display->dpy, "_NET_WM_STATE_STAYS_ON_TOP", False); + XSendEvent(display->dpy, display->root, False, SubstructureRedirectMask, &e); XFree(prop); return; } @@ -119,292 +124,266 @@ stay_on_top(struct osd_state *osd) DBG("stay_on_top: WM does not support any known protocol\n"); } -struct osd_set osd_set_new(Display *dpy) +struct osd_abstract display_state_new(Display *dpy,int width,int height,int x,int y) { - struct osd_set set; - set.len=0; - - FILE * f = popen("xrandr | grep \\ connected","r"); - char line[1000]; - while(fscanf(f," %1000[^\n]",line)==1) - { - int width,height,x,y; - char *l = line; - while(*l && !(*l==' ')) l++; - while(*l && !('0'<=*l&&*l<='9')) l++; - if(sscanf(l,"%dx%d+%d+%d",&width,&height,&x,&y)==4) - if(set.len < OSD_MAX_SET_LEN) - { - DBG("ADD screen %dx%d on %d,%d\n",width,height,x,y); - set.state[set.len++] = osd_state_new(dpy,width,height,x,y); - } - } - return set; -} - -struct osd_state *osd_state_new(Display *dpy,int width,int height,int x,int y) -{ - struct osd_state *osd = xmalloc(sizeof(*osd)); - memset(osd, 0, sizeof(*osd)); + struct display_state *display = xmalloc(sizeof(*display)); + memset(display, 0, sizeof(*display)); - osd->dpy = dpy; - osd->screen = XDefaultScreen(osd->dpy); - osd->visual = XDefaultVisual(osd->dpy, osd->screen); - osd->cmap = DefaultColormap(osd->dpy, osd->screen); - osd->root = DefaultRootWindow(osd->dpy); + display->dpy = dpy; + display->screen = XDefaultScreen(display->dpy); + display->visual = XDefaultVisual(display->dpy, display->screen); + display->cmap = DefaultColormap(display->dpy, display->screen); + display->root = DefaultRootWindow(display->dpy); // FIXME: These can change. And what about Xinerama? - osd->depth = XDefaultDepth(osd->dpy, osd->screen); - osd->screen_width = width; - osd->screen_height = height; - osd->screen_x = x; - osd->screen_y = y; - DBG("Screen: %dx%d depth %d\n", osd->screen_width, osd->screen_height, osd->depth); + display->depth = XDefaultDepth(display->dpy, display->screen); + display->screen_width = width; + display->screen_height = height; + display->screen_x = x; + display->screen_y = y; + DBG("Screen: %dx%d depth %d\n", display->screen_width, display->screen_height, display->depth); int event_basep, error_basep; - if (!XShapeQueryExtension(osd->dpy, &event_basep, &error_basep)) + if (!XShapeQueryExtension(display->dpy, &event_basep, &error_basep)) die("XShape extension not supported by X server, giving up"); - osd->max_lines = 4; - osd->lines = xmalloc(sizeof(struct osd_line) * osd->max_lines); - - return osd; + display->max_lines = 4; + display->num_lines = 0; + display->lines = xmalloc(sizeof(struct display_line) * display->max_lines); + + display_set_font(display, font_name, line_spacing); + + struct osd_abstract r; + memset(&r, 0, sizeof(r)); + r.context = display; + void (*show)(struct display_state*, struct osd_line*, int) = display_show; + r.show = (void (*)(void*, struct osd_line*, int)) show; + void (*clear)(struct display_state*) = display_clear; + r.clear = (void (*)(void*)) clear; + bool (*handle_event)(struct display_state*, XEvent *) = display_handle_event; + r.handle_event = (bool (*)(void*, XEvent *)) handle_event; + return r; } -void osd_free(struct osd_state *osd) +void display_free(struct display_state *display) { - osd_hide(osd); + display_hide(display); - if (osd->font) - XftFontClose(osd->dpy, osd->font); + if (display->font) + XftFontClose(display->dpy, display->font); - free(osd->lines); - free(osd); + free(display->lines); + free(display); } -void osd_set_font(struct osd_state *osd, char *font_name, double line_spacing) +void display_set_font(struct display_state *display, char *font_name, double line_spacing) { - if (osd->font) - XftFontClose(osd->dpy, osd->font); + if (display->font) + XftFontClose(display->dpy, display->font); DBG("Using font %s\n", font_name); - osd->font = XftFontOpenName(osd->dpy, osd->screen, font_name); - if (!osd->font) + display->font = XftFontOpenName(display->dpy, display->screen, font_name); + if (!display->font) die("Cannot open font %s", font_name); - DBG("Font: asc=%d desc=%d ht=%d\n", osd->font->ascent, osd->font->descent, osd->font->height); + DBG("Font: asc=%d desc=%d ht=%d\n", display->font->ascent, display->font->descent, display->font->height); - osd->line_distance = osd->font->height; - osd->line_height = osd->font->ascent; - osd->line_skip = osd->line_distance * line_spacing; - DBG("Line: distance=%d height=%d skip=%d\n", osd->line_distance, osd->line_height, osd->line_skip); + display->line_distance = display->font->height; + display->line_height = display->font->ascent; + display->line_skip = display->line_distance * line_spacing; + DBG("Line: distance=%d height=%d skip=%d\n", display->line_distance, display->line_height, display->line_skip); } -struct osd_line *osd_add_line(struct osd_state *osd, enum osd_line_type type) +struct display_line *display_add_line(struct display_state *display, struct osd_line * line) { - if (osd->num_lines >= osd->max_lines) + if (display->num_lines >= display->max_lines) { - osd->max_lines = 2 * osd->max_lines; - osd->lines = xrealloc(osd->lines, sizeof(struct osd_line) * osd->max_lines); + display->max_lines = 2 * display->max_lines; + display->lines = xrealloc(display->lines, sizeof(struct display_line) * display->max_lines); } - struct osd_line *l = &osd->lines[osd->num_lines++]; - l->type = type; - l->fg_color = "green"; - l->outline_color = "black"; - l->outline_width = 0; - - switch (l->type) - { - case OSD_TYPE_TEXT: - l->u.text[0] = 0; - break; - case OSD_TYPE_PERCENTAGE: - case OSD_TYPE_SLIDER: - l->u.percent = 0; - break; - default: - die("osd_add_line: unknown type %d", type); - } + struct display_line *l = &display->lines[display->num_lines++]; + fprintf(stderr,"EZD %lld\n",(long long)display); + l->line = line; return l; } -static void osd_prepare_line(struct osd_state *osd, int i) +static void display_prepare_line(struct display_state *display, int i) { - struct osd_line *line = &osd->lines[i]; - switch (line->type) + struct display_line *line = &display->lines[i]; + switch (line->line->type) { case OSD_TYPE_TEXT: { XGlyphInfo gi; - XftTextExtentsUtf8(osd->dpy, osd->font, (unsigned char *) line->u.text, strlen(line->u.text), &gi); + XftTextExtentsUtf8(display->dpy, display->font, (unsigned char *) line->line->u.text, strlen(line->line->u.text), &gi); DBG("Line #%d: Glyph info: (%d,%d)+(%d,%d) off (%d,%d)\n", i, gi.x, gi.y, gi.width, gi.height, gi.xOff, gi.yOff); - line->width = gi.width + 2*line->outline_width; - line->height = osd->line_distance + 2*line->outline_width; + line->width = gi.width + 2*line->line->outline_width; + line->height = display->line_distance + 2*line->line->outline_width; break; } case OSD_TYPE_PERCENTAGE: case OSD_TYPE_SLIDER: { #ifdef SLIDERS_WITH_BRACKETS - line->slider_unit = osd->line_height / 5; - line->slider_space = osd->line_height / 5; + line->slider_unit = display->line_height / 5; + line->slider_space = display->line_height / 5; #else - line->slider_unit = osd->line_height / 3; - line->slider_space = osd->line_height / 6; + line->slider_unit = display->line_height / 3; + line->slider_space = display->line_height / 6; #endif if (!line->slider_space) line->slider_space = line->slider_unit = 1; - int use_width = osd->screen_width * 4 / 5; + int use_width = display->screen_width * 4 / 5; int u = line->slider_unit + line->slider_space; line->slider_units = (use_width + line->slider_space) / u; if (line->slider_units < 3) line->slider_units = 3; - line->width = line->slider_units*u - line->slider_space + 2*line->outline_width; - line->height = osd->line_height + 2*line->outline_width; + line->width = line->slider_units*u - line->slider_space + 2*line->line->outline_width; + line->height = display->line_height + 2*line->line->outline_width; break; } default: - die("osd_recalc_line: unknown type %d", line->type); + die("display_recalc_line: unknown type %d", line->line->type); } - DBG("Line #%d: Width %d (outline %d)\n", i, line->width, line->outline_width); + DBG("Line #%d: Width %d (outline %d)\n", i, line->width, line->line->outline_width); } -static void osd_justify_line(struct osd_state *osd, int i) +static void display_justify_line(struct display_state *display, int i) { // FIXME: Support more modes of justification - struct osd_line *line = &osd->lines[i]; - line->x_pos = (osd->win_width - line->width) / 2; + struct display_line *line = &display->lines[i]; + line->x_pos = (display->win_width - line->width) / 2; DBG("Line #%d: Position (%d,%d)\n", i, line->x_pos, line->y_pos); } -static void osd_prepare(struct osd_state *osd) +static void display_prepare(struct display_state *display) { - osd->win_width = 0; - osd->win_height = 0; - for (int i=0; i < osd->num_lines; i++) + display->win_width = 0; + display->win_height = 0; + for (int i=0; i < display->num_lines; i++) { - struct osd_line *line = &osd->lines[i]; - osd_prepare_line(osd, i); - if (line->width > osd->win_width) - osd->win_width = line->width; - osd->win_height += line->height; + struct display_line *line = &display->lines[i]; + display_prepare_line(display, i); + if (line->width > display->win_width) + display->win_width = line->width; + display->win_height += line->height; if (i) - osd->win_height += osd->line_skip; + display->win_height += display->line_skip; } - if (osd->win_width > osd->screen_width) - osd->win_width = osd->screen_width; - if (osd->win_height > osd->screen_height) - osd->win_height = osd->screen_height; - DBG("Window size set to %dx%d\n", osd->win_width, osd->win_height); + if (display->win_width > display->screen_width) + display->win_width = display->screen_width; + if (display->win_height > display->screen_height) + display->win_height = display->screen_height; + DBG("Window size set to %dx%d\n", display->win_width, display->win_height); int y = 0; - for (int i=0; i < osd->num_lines; i++) + for (int i=0; i < display->num_lines; i++) { - struct osd_line *line = &osd->lines[i]; + struct display_line *line = &display->lines[i]; if (i) - y += osd->line_skip; + y += display->line_skip; line->y_pos = y; - osd_justify_line(osd, i); + display_justify_line(display, i); y += line->height; } } -static void osd_draw_box(struct osd_state *osd, struct osd_line *line, int x, int y, int w, int h) +static void display_draw_box(struct display_state *display, struct display_line *line, int x, int y, int w, int h) { - XftDrawRect(osd->mask_draw, &osd->mask_color, - x - line->outline_width, y - line->outline_width, - w + 2*line->outline_width, h + 2*line->outline_width); - XftDrawRect(osd->image_draw, &osd->fg_color, x, y, w, h); + XftDrawRect(display->mask_draw, &display->mask_color, + x - line->line->outline_width, y - line->line->outline_width, + w + 2*line->line->outline_width, h + 2*line->line->outline_width); + XftDrawRect(display->image_draw, &display->fg_color, x, y, w, h); } -static void osd_draw_line(struct osd_state *osd, int i) +static void display_draw_line(struct display_state *display, int i) { - struct osd_line *line = &osd->lines[i]; + struct display_line *line = &display->lines[i]; // Allocate colors XftColor outline_color; XRenderColor mask_rc = { .red = 0xffff, .green = 0xffff, .blue = 0xffff, .alpha = 0xffff }; - if (!XftColorAllocName(osd->dpy, osd->visual, osd->cmap, line->fg_color, &osd->fg_color) || - !XftColorAllocName(osd->dpy, osd->visual, osd->cmap, line->outline_color, &outline_color) || - !XftColorAllocValue(osd->dpy, osd->visual, osd->cmap, &mask_rc, &osd->mask_color)) + if (!XftColorAllocName(display->dpy, display->visual, display->cmap, line->line->fg_color, &display->fg_color) || + !XftColorAllocName(display->dpy, display->visual, display->cmap, line->line->outline_color, &outline_color) || + !XftColorAllocValue(display->dpy, display->visual, display->cmap, &mask_rc, &display->mask_color)) die("Cannot allocate colors"); // Draw background in outline color - XftDrawRect(osd->image_draw, &outline_color, 0, line->y_pos, osd->win_width, line->height); + XftDrawRect(display->image_draw, &outline_color, 0, line->y_pos, display->win_width, line->height); - switch (line->type) + switch (line->line->type) { case OSD_TYPE_TEXT: { - int x = line->x_pos + line->outline_width; - int y = line->y_pos + line->outline_width + osd->line_height; + int x = line->x_pos + line->line->outline_width; + int y = line->y_pos + line->line->outline_width + display->line_height; - unsigned char *text = (unsigned char *) line->u.text; - int text_len = strlen(line->u.text); - XftDrawStringUtf8(osd->image_draw, &osd->fg_color, osd->font, x, y, text, text_len); + unsigned char *text = (unsigned char *) line->line->u.text; + int text_len = strlen(line->line->u.text); + XftDrawStringUtf8(display->image_draw, &display->fg_color, display->font, x, y, text, text_len); // This is slow, but unlike the method used by libxosd, the result isn't ugly. - int outline = line->outline_width; + int outline = line->line->outline_width; for (int dx = -outline; dx <= outline; dx++) for (int dy = -outline; dy <= outline; dy++) if (dx*dx + dy*dy <= outline*outline) - XftDrawStringUtf8(osd->mask_draw, &osd->mask_color, osd->font, x + dx, y + dy, text, text_len); + XftDrawStringUtf8(display->mask_draw, &display->mask_color, display->font, x + dx, y + dy, text, text_len); break; } case OSD_TYPE_PERCENTAGE: case OSD_TYPE_SLIDER: { - int x = line->x_pos + line->outline_width; - int y = line->y_pos + line->outline_width; + int x = line->x_pos + line->line->outline_width; + int y = line->y_pos + line->line->outline_width; int su = line->slider_unit; int advance = su + line->slider_space; int units = line->slider_units; #ifdef SLIDERS_WITH_BRACKETS units -= 2; - int hu = osd->line_height / 5; + int hu = display->line_height / 5; int hd = 2*hu; - osd_draw_box(osd, line, x, y, su - su/3, 5*hu); - osd_draw_box(osd, line, x, y, su, hu); - osd_draw_box(osd, line, x, y + 4*hu, su, hu); + display_draw_box(display, line, x, y, su - su/3, 5*hu); + display_draw_box(display, line, x, y, su, hu); + display_draw_box(display, line, x, y + 4*hu, su, hu); x += advance; #else - int hu = osd->line_height / 3; + int hu = display->line_height / 3; int hd = hu; #endif int min, max; - if (line->type == OSD_TYPE_PERCENTAGE) + if (line->line->type == OSD_TYPE_PERCENTAGE) { min = 0; - max = (units+1) * line->u.percent / 100 - 1; + max = (units+1) * line->line->u.percent / 100 - 1; } else - min = max = (units-1) * line->u.percent / 100; + min = max = (units-1) * line->line->u.percent / 100; for (int i=0; i < units; i++) { if (i >= min && i <= max) - osd_draw_box(osd, line, x, y, su, osd->line_height); + display_draw_box(display, line, x, y, su, display->line_height); else - osd_draw_box(osd, line, x, y + hd, su, hu); + display_draw_box(display, line, x, y + hd, su, hu); x += advance; } #ifdef SLIDERS_WITH_BRACKETS - osd_draw_box(osd, line, x + su/3, y, su - su/3, 5*hu); - osd_draw_box(osd, line, x, y, su, hu); - osd_draw_box(osd, line, x, y + 4*hu, su, hu); + display_draw_box(display, line, x + su/3, y, su - su/3, 5*hu); + display_draw_box(display, line, x, y, su, hu); + display_draw_box(display, line, x, y + 4*hu, su, hu); #endif break; } default: - die("osd_draw_line: unknown type %d", line->type); + die("display_draw_line: unknown type %d", line->line->type); } - XftColorFree(osd->dpy, osd->visual, osd->cmap, &osd->fg_color); - XftColorFree(osd->dpy, osd->visual, osd->cmap, &outline_color); - XftColorFree(osd->dpy, osd->visual, osd->cmap, &osd->mask_color); + XftColorFree(display->dpy, display->visual, display->cmap, &display->fg_color); + XftColorFree(display->dpy, display->visual, display->cmap, &outline_color); + XftColorFree(display->dpy, display->visual, display->cmap, &display->mask_color); } static void repace_bad_char(char * in) @@ -420,11 +399,11 @@ static void repace_bad_char(char * in) } } -static void osd_log_to_file(struct osd_state *osd) +static void display_log_to_file(struct display_state *display) { #define MAXIMUM_FILE_MANE_LEN 1024 int need_log=0; - for(int i=0;i<osd->num_lines;i++) need_log|=osd->lines[i].log; + for(int i=0;i<display->num_lines;i++) need_log|=display->lines[i].line->log; if(need_log&1) { char name[MAXIMUM_FILE_MANE_LEN]; @@ -433,22 +412,22 @@ static void osd_log_to_file(struct osd_state *osd) if(f) { fprintf(f,"%lld\n",(long long)time(0)); - for(int i=0;i<osd->num_lines;i++) + for(int i=0;i<display->num_lines;i++) { - struct osd_line * line = osd->lines+i; + struct osd_line * line = display->lines[i].line; if(!(line->log & 1)) continue; if(line->type!=OSD_TYPE_TEXT) continue; // Allocate colors XftColor outline_color; XRenderColor mask_rc = { .red = 0xffff, .green = 0xffff, .blue = 0xffff, .alpha = 0xffff }; - if (!XftColorAllocName(osd->dpy, osd->visual, osd->cmap, line->fg_color, &osd->fg_color) || - !XftColorAllocName(osd->dpy, osd->visual, osd->cmap, line->outline_color, &outline_color) || - !XftColorAllocValue(osd->dpy, osd->visual, osd->cmap, &mask_rc, &osd->mask_color)) + if (!XftColorAllocName(display->dpy, display->visual, display->cmap, line->fg_color, &display->fg_color) || + !XftColorAllocName(display->dpy, display->visual, display->cmap, line->outline_color, &outline_color) || + !XftColorAllocValue(display->dpy, display->visual, display->cmap, &mask_rc, &display->mask_color)) die("Cannot allocate colors"); - typeof(osd->fg_color.color) c = osd->fg_color.color; - //fprintf(stderr,"%02x%02x%02x %s\n",c.red/256,c.green/256,c.blue/256,osd->lines[i].u.text); + typeof(display->fg_color.color) c = display->fg_color.color; + //fprintf(stderr,"%02x%02x%02x %s\n",c.red/256,c.green/256,c.blue/256,display->lines[i].u.text); char text[1024]; - snprintf(text,1000,osd->lines[i].u.text); + snprintf(text,1000,display->lines[i].line->u.text); repace_bad_char(text); fprintf(f,"%02x%02x%02x %s\n",c.red/256,c.green/256,c.blue/256,text); } @@ -465,20 +444,20 @@ static void osd_log_to_file(struct osd_state *osd) time_t t = time(NULL); struct tm tm = *localtime(&t); fprintf(f,"%04d-%02d-%02d %02d:%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); - for(int i=0;i<osd->num_lines;i++) + for(int i=0;i<display->num_lines;i++) { - struct osd_line * line = osd->lines+i; + struct osd_line * line = display->lines[i].line; if(!(line->log & 2)) continue; // Allocate colors XftColor outline_color; XRenderColor mask_rc = { .red = 0xffff, .green = 0xffff, .blue = 0xffff, .alpha = 0xffff }; - if (!XftColorAllocName(osd->dpy, osd->visual, osd->cmap, line->fg_color, &osd->fg_color) || - !XftColorAllocName(osd->dpy, osd->visual, osd->cmap, line->outline_color, &outline_color) || - !XftColorAllocValue(osd->dpy, osd->visual, osd->cmap, &mask_rc, &osd->mask_color)) + if (!XftColorAllocName(display->dpy, display->visual, display->cmap, line->fg_color, &display->fg_color) || + !XftColorAllocName(display->dpy, display->visual, display->cmap, line->outline_color, &outline_color) || + !XftColorAllocValue(display->dpy, display->visual, display->cmap, &mask_rc, &display->mask_color)) die("Cannot allocate colors"); - typeof(osd->fg_color.color) c = osd->fg_color.color; - //fprintf(stderr,"%02x%02x%02x %s\n",c.red/256,c.green/256,c.blue/256,osd->lines[i].u.text); - fprintf(f," \x1b[38;2;%d;%d;%dm%s\e[0m",c.red/256,c.green/256,c.blue/256,osd->lines[i].u.text); + typeof(display->fg_color.color) c = display->fg_color.color; + //fprintf(stderr,"%02x%02x%02x %s\n",c.red/256,c.green/256,c.blue/256,display->lines[i].u.text); + fprintf(f," \x1b[38;2;%d;%d;%dm%s\e[0m",c.red/256,c.green/256,c.blue/256,display->lines[i].line->u.text); } fprintf(f,"\n"); fclose(f); @@ -486,100 +465,103 @@ static void osd_log_to_file(struct osd_state *osd) } } -void osd_show(struct osd_state *osd) +void display_show(struct display_state *display, struct osd_line * lines, int num_lines) { - osd_hide(osd); - osd_prepare(osd); - osd_log_to_file(osd); + display_hide(display); + + for(int i=0; i<num_lines; i++) display_add_line(display,lines+i); + + display_prepare(display); + display_log_to_file(display); // Create our window XSetWindowAttributes win_attr = { .override_redirect = 1, }; - osd->win = XCreateWindow(osd->dpy, - osd->root, - (osd->screen_width - osd->win_width) / 2 + osd->screen_x, (osd->screen_height - osd->win_height) / 2 + osd->screen_y, - osd->win_width, osd->win_height, + display->win = XCreateWindow(display->dpy, + display->root, + (display->screen_width - display->win_width) / 2 + display->screen_x, (display->screen_height - display->win_height) / 2 + display->screen_y, + display->win_width, display->win_height, 0, - osd->depth, + display->depth, CopyFromParent, - osd->visual, + display->visual, CWOverrideRedirect, &win_attr); - XStoreName(osd->dpy, osd->win, "OSD"); - stay_on_top(osd); + XStoreName(display->dpy, display->win, "OSD"); + stay_on_top(display); // Create image pixmap and its graphic context - // osd->gc can be used for both osd->win and osd->image_bitmap as they have the same root and depth - osd->image_pixmap = XCreatePixmap(osd->dpy, osd->win, osd->win_width, osd->win_height, osd->depth); + // display->gc can be used for both display->win and display->image_bitmap as they have the same root and depth + display->image_pixmap = XCreatePixmap(display->dpy, display->win, display->win_width, display->win_height, display->depth); XGCValues gcv = { .graphics_exposures = 0, }; - osd->gc = XCreateGC(osd->dpy, osd->win, GCGraphicsExposures, &gcv); + display->gc = XCreateGC(display->dpy, display->win, GCGraphicsExposures, &gcv); // Create mask bitmap and its GC - osd->mask_bitmap = XCreatePixmap(osd->dpy, osd->win, osd->win_width, osd->win_height, 1); - osd->mask_gc = XCreateGC(osd->dpy, osd->mask_bitmap, GCGraphicsExposures, &gcv); + display->mask_bitmap = XCreatePixmap(display->dpy, display->win, display->win_width, display->win_height, 1); + display->mask_gc = XCreateGC(display->dpy, display->mask_bitmap, GCGraphicsExposures, &gcv); // Clear the mask bitmap - XSetBackground(osd->dpy, osd->mask_gc, WhitePixel(osd->dpy, osd->screen)); - XSetForeground(osd->dpy, osd->mask_gc, BlackPixel(osd->dpy, osd->screen)); - XFillRectangle(osd->dpy, osd->mask_bitmap, osd->mask_gc, 0, 0, osd->win_width, osd->win_height); + XSetBackground(display->dpy, display->mask_gc, WhitePixel(display->dpy, display->screen)); + XSetForeground(display->dpy, display->mask_gc, BlackPixel(display->dpy, display->screen)); + XFillRectangle(display->dpy, display->mask_bitmap, display->mask_gc, 0, 0, display->win_width, display->win_height); // Create XftDraw for mask and image - osd->mask_draw = XftDrawCreateBitmap(osd->dpy, osd->mask_bitmap); - osd->image_draw = XftDrawCreate(osd->dpy, osd->image_pixmap, osd->visual, osd->cmap); - if (!osd->mask_draw || !osd->image_draw) + display->mask_draw = XftDrawCreateBitmap(display->dpy, display->mask_bitmap); + display->image_draw = XftDrawCreate(display->dpy, display->image_pixmap, display->visual, display->cmap); + if (!display->mask_draw || !display->image_draw) die("Cannot create XftDraw"); // Draw individial lines - for (int i=0; i < osd->num_lines; i++) - osd_draw_line(osd, i); + for (int i=0; i < display->num_lines; i++) + display_draw_line(display, i); - XShapeCombineMask(osd->dpy, osd->win, ShapeBounding, 0, 0, osd->mask_bitmap, ShapeSet); + XShapeCombineMask(display->dpy, display->win, ShapeBounding, 0, 0, display->mask_bitmap, ShapeSet); - XSelectInput(osd->dpy, osd->win, ExposureMask); - XMapRaised(osd->dpy, osd->win); - XFlush(osd->dpy); + XSelectInput(display->dpy, display->win, ExposureMask); + XMapRaised(display->dpy, display->win); + XFlush(display->dpy); - osd->visible = 1; + display->visible = 1; } -void osd_hide(struct osd_state *osd) +void display_hide(struct display_state *display) { - if (!osd->visible) + if (!display->visible) return; - XftDrawDestroy(osd->image_draw); - XftDrawDestroy(osd->mask_draw); - XFreeGC(osd->dpy, osd->gc); - XFreeGC(osd->dpy, osd->mask_gc); - XFreePixmap(osd->dpy, osd->image_pixmap); - XFreePixmap(osd->dpy, osd->mask_bitmap); - XDestroyWindow(osd->dpy, osd->win); - XFlush(osd->dpy); + XftDrawDestroy(display->image_draw); + XftDrawDestroy(display->mask_draw); + XFreeGC(display->dpy, display->gc); + XFreeGC(display->dpy, display->mask_gc); + XFreePixmap(display->dpy, display->image_pixmap); + XFreePixmap(display->dpy, display->mask_bitmap); + XDestroyWindow(display->dpy, display->win); + XFlush(display->dpy); - osd->visible = 0; + display->visible = 0; } -void osd_clear(struct osd_state *osd) +void display_clear(struct display_state *display) { - osd_hide(osd); - osd->num_lines = 0; + display_hide(display); + display->num_lines = 0; } -bool osd_handle_event(struct osd_state *osd, XEvent *ev) +bool display_handle_event(struct display_state *display, XEvent *ev) { - if (!osd->visible) + if (!display->visible) return 0; if (ev->type == Expose) { XExposeEvent *ex = &ev->xexpose; - if (ex->window == osd->win) + if (ex->window == display->win) { DBG("Expose cnt=%d (%d,%d)+(%d,%d)\n", ex->count, ex->x, ex->y, ex->width, ex->height); - XCopyArea(osd->dpy, osd->image_pixmap, osd->win, osd->gc, ex->x, ex->y, ex->width, ex->height, ex->x, ex->y); + XCopyArea(display->dpy, display->image_pixmap, display->win, display->gc, ex->x, ex->y, ex->width, ex->height, ex->x, ex->y); return 1; } } diff --git a/display.h b/display.h index 131f7c3ee402344837acb433acfa0a5f94e16be8..550f1c49b797c02a7d5f151774ed04f54f096f0e 100644 --- a/display.h +++ b/display.h @@ -8,26 +8,14 @@ #include <stdbool.h> #include <X11/Xlib.h> -struct osd_state; +#include "osdd-set.h" -#define OSD_MAX_LINE_LEN 256 - -enum osd_line_type { - OSD_TYPE_TEXT, - OSD_TYPE_PERCENTAGE, - OSD_TYPE_SLIDER, -}; +struct display_state; -struct osd_line { - enum osd_line_type type; - char *fg_color; - char *outline_color; - int outline_width; - union { // Data dependent on type - char text[OSD_MAX_LINE_LEN]; // in UTF-8 - unsigned int percent; // 0..100 for percentages and slider - } u; +#define OSD_MAX_LINE_LEN 256 +struct display_line { + struct osd_line * line; // Used internally int width; int height; @@ -36,22 +24,13 @@ struct osd_line { int slider_unit; int slider_space; int slider_units; - int log; -}; - -#define ALL_OSD_SET(set) for(int ALL_OSD_SET_i=0;ALL_OSD_SET_i<set.len;ALL_OSD_SET_i++) set->state[ALL_OSD_SET_i] -#define OSD_MAX_SET_LEN 10 -struct osd_set { - struct osd_state* state[OSD_MAX_SET_LEN]; - int len; }; -struct osd_state *osd_state_new(Display *dpy,int width,int height,int x,int y); -struct osd_set osd_set_new(Display *dpy); -void osd_free(struct osd_state *osd); -void osd_set_font(struct osd_state *osd, char *font_name, double line_spacing); -struct osd_line *osd_add_line(struct osd_state *osd, enum osd_line_type type); -void osd_show(struct osd_state *osd); -void osd_hide(struct osd_state *osd); -void osd_clear(struct osd_state *osd); -bool osd_handle_event(struct osd_state *osd, XEvent *ev); +struct osd_abstract display_state_new(Display *dpy,int width,int height,int x,int y); +void display_free(struct display_state *display); +void display_set_font(struct display_state *display, char *font_name, double line_spacing); +struct display_line *display_add_line(struct display_state *display, struct osd_line * line); +void display_show(struct display_state *display, struct osd_line * lines, int num_lines); +void display_hide(struct display_state *display); +void display_clear(struct display_state *display); +bool display_handle_event(struct display_state *display, XEvent *ev); diff --git a/osdd-set.c b/osdd-set.c new file mode 100644 index 0000000000000000000000000000000000000000..629228eb4711fdfc2d2793f7d65063a7ff30d5af --- /dev/null +++ b/osdd-set.c @@ -0,0 +1,81 @@ +/* + * On-screen Display +* + * (c) 2013--2014 Martin Mares <mj@ucw.cz> + * (c) 2020--2021 Jiri Kalvoda <jirikalvoda@kam.mff.cuni.cz> + */ + +#include <stdio.h> + +#include "osdd-set.h" +#include "util.h" + + +struct osd_line *osd_set_add_line(struct osd_set *set, enum osd_line_type type) +{ + if (set->num_lines >= set->max_lines) + { + set->max_lines = 2 * set->max_lines + 1; + set->lines = xrealloc(set->lines, sizeof(struct osd_line) * set->max_lines); + } + + struct osd_line *l = &set->lines[set->num_lines++]; + l->type = type; + l->fg_color = "green"; + l->outline_color = "black"; + l->outline_width = 0; + + switch (l->type) + { + case OSD_TYPE_TEXT: + l->u.text[0] = 0; + break; + case OSD_TYPE_PERCENTAGE: + case OSD_TYPE_SLIDER: + l->u.percent = 0; + break; + default: + die("osd_add_line: unknown type %d", type); + } + + return l; +} + +void osd_set_show(struct osd_set *set) +{ + for(int i=0;i<set->len;i++) + { + set->elements[i].show(set->elements[i].context,set->lines,set->num_lines); + } +} + +void osd_set_clear(struct osd_set *set) +{ + for(int i=0;i<set->len;i++) + { + set->elements[i].clear(set->elements[i].context); + } + set->num_lines = 0; +} + +bool osd_set_handle_event(struct osd_set *set, XEvent *ev) +{ + bool r=0; + for(int i=0;i<set->len;i++) + { + if(set->elements[i].handle_event) r |= set->elements[i].handle_event(set->elements[i].context,ev); + } + return r; +} + +struct osd_set osd_set_new(void) +{ + struct osd_set set; + set.len=0; + + set.num_lines=0; + set.max_lines=0; + set.lines=0; + return set; +} + diff --git a/osdd-set.h b/osdd-set.h new file mode 100644 index 0000000000000000000000000000000000000000..348ff1a0b42f67c85fb592f00b63148d7056aded --- /dev/null +++ b/osdd-set.h @@ -0,0 +1,62 @@ +/* + * On-screen Display + * + * (c) 2013--2014 Martin Mares <mj@ucw.cz> + * (c) 2020--2021 Jiri Kalvoda <jirikalvoda@kam.mff.cuni.cz> + */ + +#ifndef OSDD_SET_H +#define OSDD_SET_H + +#include <stdbool.h> +#include <X11/Xlib.h> + +struct display_state; + +#define OSD_MAX_LINE_LEN 256 +#define OSD_MAX_SET_LEN 64 + +#define OSD_ABSTRACT_NAME_LEN 64 + +enum osd_line_type { + OSD_TYPE_TEXT, + OSD_TYPE_PERCENTAGE, + OSD_TYPE_SLIDER, +}; + +struct osd_line { + enum osd_line_type type; + char *fg_color; + char *outline_color; + int outline_width; + union { // Data dependent on type + char text[OSD_MAX_LINE_LEN]; // in UTF-8 + unsigned int percent; // 0..100 for percentages and slider + } u; + int log; +}; + +struct osd_abstract { + void * context; + + void (*show)(void * context, struct osd_line * lines, int num_lines); + void (*clear)(void * context); + bool (*handle_event)(void * context, XEvent *ev); +}; + +struct osd_set { + struct osd_abstract elements[OSD_MAX_SET_LEN]; + int len; + + struct osd_line *lines; + int num_lines; + int max_lines; +}; + +struct osd_set osd_set_new(void); +struct osd_line *osd_set_add_line(struct osd_set *set, enum osd_line_type type); +void osd_set_show(struct osd_set *set); +void osd_set_clear(struct osd_set *set); +bool osd_set_handle_event(struct osd_set *set, XEvent *ev); + +#endif diff --git a/osdd.c b/osdd.c index a92fbe06d0138ef2fc181fd85dd488bde5d6690b..e9dc4afee1dd991f387a09ec65af0757ebfcf358 100644 --- a/osdd.c +++ b/osdd.c @@ -15,10 +15,12 @@ #include <X11/Xlib.h> #include <X11/Xatom.h> -#undef DEBUG +#define DEBUG #include "util.h" +#include "osdd-set.h" #include "display.h" + static struct osd_set osd; #define FOREACHOSD for(int i=0;i<osd.len;i++) @@ -26,7 +28,6 @@ static timestamp_t now; /*** Options ***/ -static char *font_name = "times-64:bold"; static char *default_color = "green"; static char *default_outline_color = "black"; static int default_outline_width = 2; @@ -35,7 +36,6 @@ static int default_min_duration = 250; static int default_log = 255; static int debug_mode; static int test_mode; -static double line_spacing = 0.2; static const char short_opts[] = "c:d:Df:m:o:O:s:"; @@ -92,10 +92,10 @@ parse_opts(int argc, char **argv) debug_mode = 1; break; case 'f': - font_name = optarg; + //font_name = optarg; break; case 'l': - line_spacing = atof(optarg); + //line_spacing = atof(optarg); break; case 'm': default_min_duration = atoi(optarg); @@ -144,7 +144,7 @@ display_msg(struct msg *msg) int num_l=0; while (*line) { - // The parser it destructive, but it does not do any harm, since we display each message only once. + // The parser is destructive, but it does not do any harm, since we display each message only once. char *nl = strchr(line, '\n'); *nl++ = 0; @@ -162,22 +162,21 @@ display_msg(struct msg *msg) } DBG("\t%s:%s\n", key, val); - struct osd_line *l[OSD_MAX_SET_LEN]; - FOREACHOSD l[i]=0; + struct osd_line *l=0; if (!key[0]) { - FOREACHOSD l[i] = osd_add_line(osd.state[i], OSD_TYPE_TEXT); - FOREACHOSD sprintf(l[i]->u.text, "%.*s", OSD_MAX_LINE_LEN, val); + l = osd_set_add_line(&osd, OSD_TYPE_TEXT); + sprintf(l->u.text, "%.*s", OSD_MAX_LINE_LEN, val); } else if (!strcmp(key, "percentage") || !strcmp(key, "percent")) { - FOREACHOSD l[i] = osd_add_line(osd.state[i], OSD_TYPE_PERCENTAGE); - FOREACHOSD l[i]->u.percent = atoi(val); + l = osd_set_add_line(&osd, OSD_TYPE_PERCENTAGE); + l->u.percent = atoi(val); } else if (!strcmp(key, "slider")) { - FOREACHOSD l[i] = osd_add_line(osd.state[i], OSD_TYPE_SLIDER); - FOREACHOSD l[i]->u.percent = atoi(val); + l = osd_set_add_line(&osd, OSD_TYPE_SLIDER); + l->u.percent = atoi(val); } else if (!strcmp(key, "duration")) msg->max_light = now + atoi(val); @@ -191,21 +190,21 @@ display_msg(struct msg *msg) outline_color = val; else if (!strcmp(key, "outline-width")) outline_width = atoi(val); - FOREACHOSD if (l[i]) + if (l) { num_l++; - l[i]->fg_color = fg_color; - l[i]->outline_color = outline_color; - l[i]->outline_width = outline_width; - l[i]->log = i?0:log; + l->fg_color = fg_color; + l->outline_color = outline_color; + l->outline_width = outline_width; + l->log = log; } line = nl; } - if(!num_l) FOREACHOSD + if(!num_l) { - struct osd_line *l = osd_add_line(osd.state[i],OSD_TYPE_TEXT); + struct osd_line *l = osd_set_add_line(&osd,OSD_TYPE_TEXT); l->u.text[0]=0; l->fg_color = fg_color; l->outline_color = outline_color; @@ -216,7 +215,7 @@ display_msg(struct msg *msg) if (msg->min_light > msg->max_light) msg->min_light = msg->max_light; - FOREACHOSD osd_show(osd.state[i]); + osd_set_show(&osd); } static void @@ -228,7 +227,7 @@ msg_to_stdout(struct msg *msg) int out_parametr_index=0; while (*line) { - // The parser it destructive, but it does not do any harm, since we display each message only once. + // The parser is destructive, but it does not do any harm, since we display each message only once. char *nl = strchr(line, '\n'); *nl++ = 0; @@ -261,7 +260,7 @@ static void hide_msg(struct msg *msg) { DBG("## Hiding message\n"); - FOREACHOSD osd_clear(osd.state[i]); + osd_set_clear(&osd); free(msg); } @@ -400,8 +399,24 @@ main(int argc, char **argv) XFlush(dpy); } - osd = osd_set_new(dpy); - FOREACHOSD osd_set_font(osd.state[i], font_name, line_spacing); + osd = osd_set_new(); + { + FILE * f = popen("xrandr | grep \\ connected","r"); + char line[1001]; + while(fscanf(f," %1000[^\n]",line)==1) + { + int width,height,x,y; + char *l = line; + while(*l && !(*l==' ')) l++; + while(*l && !('0'<=*l&&*l<='9')) l++; + if(sscanf(l,"%dx%d+%d+%d",&width,&height,&x,&y)==4) + if(osd.len < OSD_MAX_SET_LEN) + { + DBG("ADD screen %dx%d on %d,%d\n",width,height,x,y); + osd.elements[osd.len++] = display_state_new(dpy,width,height,x,y); + } + } + } struct pollfd pfd = { .fd = ConnectionNumber(dpy), @@ -443,7 +458,7 @@ main(int argc, char **argv) { XEvent ev; XNextEvent(dpy, &ev); - FOREACHOSD if (osd_handle_event(osd.state[i], &ev)) + if (osd_set_handle_event(&osd, &ev)) continue; if (ev.type != PropertyNotify) continue;