Skip to content
Snippets Groups Projects
Commit 2ab1559f authored by Martin Mareš's avatar Martin Mareš
Browse files

Bits of new display code

parent 187ecee4
Branches
No related tags found
No related merge requests found
......@@ -3,16 +3,14 @@ ARCHIVE=osdd-$(VERSION).tar.gz
CFLAGS=-O2 -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wredundant-decls -std=gnu99
# all: osdd osdc osd-batt osd-alsa
all: test
all: osdd osdc osd-batt osd-alsa
osdd: osdd.o util.o
osdd: osdd.o util.o display.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.o: CFLAGS+=$(shell xosd-config --cflags)
osdd: LDLIBS+=$(shell xosd-config --libs)
osdd: LDLIBS+=$(shell pkg-config --libs xft) -l Xext
osdc: LDLIBS+=-lX11
osd-batt: LDLIBS+=-lX11
......@@ -20,9 +18,7 @@ osd-batt: LDLIBS+=-lX11
osd-alsa.o: CFLAGS+=$(shell pkg-config --cflags alsa)
osd-alsa: LDLIBS+=$(shell pkg-config --libs alsa) -lX11
test.o: CFLAGS+=$(shell pkg-config --cflags xft)
test: LDFLAGS+=$(shell pkg-config --libs xft) -l Xext
test: test.o util.o
display.o: CFLAGS+=$(shell pkg-config --cflags xft)
clean:
rm -f *~ *.o TAGS core osdd osdc osd-batt osd-alsa
......
display.c 0 → 100644
/*
* On-screen Display
*
* (c) 2013 Martin Mares <mj@ucw.cz>
*
* This code is heavily inspired by the libxosd library,
* which is (c) 2000, 2001 Andre Renaud <andre@ignavus.net>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <xosd.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/extensions/shape.h>
#include <X11/extensions/render.h>
#include <X11/Xft/Xft.h>
#undef DEBUG
#include "util.h"
#include "display.h"
struct osd_state {
// Basic characteristics of current display and screen
Display *dpy;
int screen;
Visual *visual;
Colormap cmap;
Window root;
int depth;
int screen_width;
int screen_height;
// Our window
Window win;
int win_width;
int win_height;
Pixmap mask_bitmap;
Pixmap image_pixmap;
GC gc;
GC mask_gc;
// Xft state
XftFont *font;
XftDraw *mask_draw;
XftDraw *image_draw;
// Contents of the display
struct osd_line *lines;
int num_lines;
int max_lines;
int line_distance;
int line_height;
bool visible;
};
static void
stay_on_top(struct osd_state *osd)
{
int format;
unsigned long nitems, bytes_after;
unsigned char *prop = NULL;
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,
&type, &format, &nitems, &bytes_after, &prop) == Success &&
nitems > 0)
{
DBG("stay_on_top: Gnome mode\n");
// FIXME: check capabilities
XClientMessageEvent e;
memset(&e, 0, sizeof(e));
e.type = ClientMessage;
e.window = osd->win;
e.message_type = XInternAtom(osd->dpy, "_WIN_LAYER", False);
e.format = 32;
e.data.l[0] = 6; // WIN_LAYER_ONTOP */
XSendEvent(osd->dpy, osd->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,
&type, &format, &nitems, &bytes_after, &prop) == Success &&
nitems > 0)
{
DBG("stay_on_top: NetWM mode\n");
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.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);
XFree(prop);
return;
}
DBG("stay_on_top: WM does not support any known protocol\n");
}
struct osd_state *osd_new(Display *dpy)
{
struct osd_state *osd = xmalloc(sizeof(*osd));
memset(osd, 0, sizeof(*osd));
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);
// FIXME: These can change. And what about Xinerama?
osd->depth = XDefaultDepth(osd->dpy, osd->screen);
osd->screen_width = XDisplayWidth(osd->dpy, osd->screen);
osd->screen_height = XDisplayHeight(osd->dpy, osd->screen);
DBG("Screen: %dx%d depth %d\n", osd->screen_width, osd->screen_height, osd->depth);
int event_basep, error_basep;
if (!XShapeQueryExtension(osd->dpy, &event_basep, &error_basep))
die("XShape extension not supported by X server, giving up");
osd->max_lines = 2; // FIXME
osd->lines = xmalloc(sizeof(struct osd_line) * osd->max_lines);
return osd;
}
void osd_free(struct osd_state *osd)
{
osd_hide(osd);
if (osd->font)
XftFontClose(osd->dpy, osd->font);
free(osd->lines);
free(osd);
}
void osd_set_font(struct osd_state *osd, char *font_name)
{
if (osd->font)
XftFontClose(osd->dpy, osd->font);
DBG("Using font %s", font_name);
osd->font = XftFontOpenName(osd->dpy, osd->screen, font_name);
if (!osd->font)
die("Cannot open font %s", font_name);
DBG("Font: asc=%d desc=%d ht=%d", osd->font->ascent, osd->font->descent, osd->font->height);
osd->line_distance = osd->font->height;
osd->line_height = osd->font->ascent;
}
struct osd_line *osd_add_line(struct osd_state *osd, enum osd_line_type type)
{
if (osd->num_lines >= osd->max_lines)
{
osd->max_lines = 2 * osd->max_lines;
osd->lines = xrealloc(osd->lines, sizeof(struct osd_line) * osd->max_lines);
}
struct osd_line *l = &osd->lines[osd->num_lines++];
l->type = type;
l->fg_color = "green";
l->outline_color = "yellow";
l->outline_width = 0;
// FIXME: Colors, alignment etc.
switch (l->type)
{
case OSD_TYPE_TEXT:
l->u.text[0] = 0;
break;
default:
die("osd_add_line: unknown type %d", type);
}
return l;
}
static void osd_prepare_line(struct osd_state *osd, int i)
{
struct osd_line *line = &osd->lines[i];
switch (line->type)
{
case OSD_TYPE_TEXT:
{
XGlyphInfo gi;
XftTextExtentsUtf8(osd->dpy, osd->font, (unsigned char *) line->u.text, strlen(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;
break;
}
default:
die("osd_recalc_line: unknown type %d", line->type);
}
DBG("Line #%d: Width %d (outline %d)\n", i, line->width, line->outline_width);
}
static void osd_justify_line(struct osd_state *osd, int i)
{
// FIXME: Support more modes of justification
struct osd_line *line = &osd->lines[i];
line->x_pos = (osd->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)
{
osd->win_width = 0;
osd->win_height = 0;
for (int i=0; i < osd->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;
}
// FIXME: Check clipping
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);
int y = 0;
for (int i=0; i < osd->num_lines; i++)
{
struct osd_line *line = &osd->lines[i];
line->y_pos = y;
osd_justify_line(osd, i);
y += line->height;
}
}
static void osd_draw_line(struct osd_state *osd, int i)
{
struct osd_line *line = &osd->lines[i];
// Allocate colors
XftColor fg_color, outline_color, mask_color;
XRenderColor mask_rc = { .red = 0xffff, .green = 0xffff, .blue = 0xffff, .alpha = 0xffff };
if (!XftColorAllocName(osd->dpy, osd->visual, osd->cmap, line->fg_color, &fg_color) ||
!XftColorAllocName(osd->dpy, osd->visual, osd->cmap, line->outline_color, &outline_color) ||
!XftColorAllocValue(osd->dpy, osd->visual, osd->cmap, &mask_rc, &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);
switch (line->type)
{
case OSD_TYPE_TEXT:
{
unsigned char *text = (unsigned char *) line->u.text;
int text_len = strlen(line->u.text);
XftDrawStringUtf8(osd->image_draw, &fg_color, osd->font, line->x_pos + line->outline_width, line->y_pos + line->outline_width, text, text_len);
// This is slow, but unlike the method used by libxosd, the result isn't ugly.
int outline = 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, &mask_color, osd->font, 100 + dx, 100 + dy, text, text_len);
break;
}
default:
die("osd_draw_line: unknown type %d", line->type);
}
XftColorFree(osd->dpy, osd->visual, osd->cmap, &fg_color);
XftColorFree(osd->dpy, osd->visual, osd->cmap, &outline_color);
XftColorFree(osd->dpy, osd->visual, osd->cmap, &mask_color);
}
void osd_show(struct osd_state *osd)
{
osd_hide(osd);
osd_prepare(osd);
// Create our window
XSetWindowAttributes win_attr = {
.override_redirect = 1,
};
osd->win = XCreateWindow(osd->dpy,
osd->root,
0, 0,
osd->win_width, osd->win_height,
0,
osd->depth,
CopyFromParent,
osd->visual,
CWOverrideRedirect,
&win_attr);
XStoreName(osd->dpy, osd->win, "OSD");
stay_on_top(osd);
// 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);
XGCValues gcv = {
.graphics_exposures = 0,
};
osd->gc = XCreateGC(osd->dpy, osd->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);
// 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);
// 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)
die("Cannot create XftDraw");
// Draw individial lines
for (int i=0; i < osd->num_lines; i++)
osd_draw_line(osd, i);
XShapeCombineMask(osd->dpy, osd->win, ShapeBounding, 0, 0, osd->mask_bitmap, ShapeSet);
XSelectInput(osd->dpy, osd->win, ExposureMask);
XMapRaised(osd->dpy, osd->win);
XFlush(osd->dpy);
osd->visible = 1;
}
void osd_hide(struct osd_state *osd)
{
if (!osd->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);
osd->visible = 0;
}
bool osd_handle_event(struct osd_state *osd, XEvent *ev)
{
if (!osd->visible)
return 0;
if (ev->type == Expose)
{
XExposeEvent *ex = &ev->xexpose;
if (ex->window == osd->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);
return 1;
}
}
return 0;
}
/*
* On-screen Display
*
* (c) 2013 Martin Mares <mj@ucw.cz>
*/
#include <stdbool.h>
#include <X11/Xlib.h>
struct osd_state;
#define OSD_MAX_LINE_LEN 256
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
} u;
int width;
int height;
int x_pos;
int y_pos;
};
struct osd_state *osd_new(Display *dpy);
void osd_free(struct osd_state *osd);
void osd_set_font(struct osd_state *osd, char *font_name);
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);
bool osd_handle_event(struct osd_state *osd, XEvent *ev);
/*
* On-screen Display Daemon
*
* (c) 2010 Martin Mares <mj@ucw.cz>
* (c) 2010--2013 Martin Mares <mj@ucw.cz>
*/
#include <stdio.h>
......@@ -19,14 +19,14 @@
#include "util.h"
#include "display.h"
static xosd *osd;
static struct osd_state *osd;
static timestamp_t now;
/*** Options ***/
static int num_lines = 4;
static char *font_name = "-bitstream-bitstream vera sans-bold-r-normal-*-*-320-*-*-p-*-*";
static int num_lines = 4; // FIXME
static char *font_name = "-bitstream-bitstream vera sans-bold-r-normal-*-*-320-*-*-p-*-*"; // FIXME
static char *default_color = "green";
static char *default_outline_color = "black";
static int default_duration = 1000;
......@@ -114,11 +114,10 @@ display_msg(struct msg *msg)
DBG("## Displaying message\n");
msg->min_light = now + default_min_duration;
msg->max_light = now + default_duration;
xosd_set_colour(osd, default_color);
xosd_set_outline_colour(osd, default_outline_color);
char *fg_color = default_color;
char *outline_color = default_outline_color;
char *line = msg->text;
int row = 0;
while (*line)
{
// The parser it destructive, but it does not do any harm, since we display each message only once.
......@@ -139,31 +138,36 @@ display_msg(struct msg *msg)
}
DBG("\t%s:%s\n", key, val);
struct osd_line *l = NULL;
if (!key[0])
{
if (row < num_lines)
xosd_display(osd, row++, XOSD_string, val);
l = osd_add_line(osd, OSD_TYPE_TEXT);
sprintf(l->u.text, "%.*s", OSD_MAX_LINE_LEN, val);
}
else if (!strcmp(key, "percentage") || !strcmp(key, "percent"))
{
if (row < num_lines)
xosd_display(osd, row++, XOSD_percentage, atoi(val));
// FIXME
// xosd_display(osd, row++, XOSD_percentage, atoi(val));
}
else if (!strcmp(key, "slider"))
{
if (row < num_lines)
xosd_display(osd, row++, XOSD_slider, atoi(val));
// FIXME
// xsd_display(osd, row++, XOSD_slider, atoi(val));
}
else if (!strcmp(key, "duration"))
msg->max_light = now + atoi(val);
else if (!strcmp(key, "min-duration"))
msg->min_light = now + atoi(val);
else if (!strcmp(key, "color"))
xosd_set_colour(osd, val);
fg_color = val; // FIXME: Need copying!
else if (!strcmp(key, "outline-color"))
xosd_set_outline_colour(osd, val);
else
xosd_display(osd, (row < num_lines ? row++ : num_lines-1), XOSD_string, "PARSE ERROR");
outline_color = val; // FIXME: Need copying!
if (l)
{
l->fg_color = fg_color;
l->outline_color = outline_color;
}
line = nl;
}
......@@ -176,9 +180,8 @@ static void
hide_msg(struct msg *msg)
{
DBG("## Hiding message\n");
for (int i=0; i<num_lines; i++)
xosd_display(osd, i, XOSD_string, "");
xosd_hide(osd);
osd_hide(osd);
// FIXME: Reset the osd state
free(msg);
}
......@@ -256,13 +259,8 @@ main(int argc, char **argv)
XDeleteProperty(dpy, win, pty);
XFlush(dpy);
osd = xosd_create(num_lines);
if (!osd)
die("Cannot initialize OSD");
xosd_set_font(osd, font_name);
xosd_set_outline_offset(osd, 2);
xosd_set_pos(osd, XOSD_middle);
xosd_set_align(osd, XOSD_center);
osd = osd_new(dpy);
osd_set_font(osd, font_name);
struct pollfd pfd = {
.fd = ConnectionNumber(dpy),
......@@ -302,6 +300,8 @@ main(int argc, char **argv)
{
XEvent ev;
XNextEvent(dpy, &ev);
if (osd_handle_event(osd, &ev))
continue;
if (ev.type != PropertyNotify)
continue;
XPropertyEvent *p = &ev.xproperty;
......
......@@ -155,7 +155,7 @@ main(int argc, char **argv)
osd->font = XftFontOpenName(osd->dpy, osd->screen, "times-64");
if (!osd->font)
die("Cannot open font");
DBG("Font: asc=%d desc=%d ht=%d", osd->font->ascent, osd->font->descent, osd->font->height);
DBG("Font: asc=%d desc=%d ht=%d\n", osd->font->ascent, osd->font->descent, osd->font->height);
osd->mask_draw = XftDrawCreateBitmap(osd->dpy, osd->mask_bitmap);
if (!osd->mask_draw)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment