dotfiles

personal configuration files and scripts
git clone https://tongong.net/git/dotfiles.git
Log | Files | Refs | README

window.c (12678B)


      1 /* Copyright 2011-2013 Bert Muennich
      2  *
      3  * This file is part of sxiv.
      4  *
      5  * sxiv is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published
      7  * by the Free Software Foundation; either version 2 of the License,
      8  * or (at your option) any later version.
      9  *
     10  * sxiv is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with sxiv.  If not, see <http://www.gnu.org/licenses/>.
     17  */
     18 
     19 #include "sxiv.h"
     20 #define _WINDOW_CONFIG
     21 #include "config.h"
     22 #include "icon/data.h"
     23 #include "utf8.h"
     24 
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <locale.h>
     28 #include <X11/cursorfont.h>
     29 #include <X11/Xatom.h>
     30 #include <X11/Xresource.h>
     31 
     32 #define RES_CLASS "Sxiv"
     33 
     34 enum {
     35 	H_TEXT_PAD = 5,
     36 	V_TEXT_PAD = 1
     37 };
     38 
     39 static struct {
     40 	int name;
     41 	Cursor icon;
     42 } cursors[CURSOR_COUNT] = {
     43 	{ XC_left_ptr }, { XC_dotbox }, { XC_watch },
     44 	{ XC_sb_left_arrow }, { XC_sb_right_arrow }
     45 };
     46 
     47 static GC gc;
     48 
     49 static XftFont *font;
     50 static int fontheight;
     51 static double fontsize;
     52 static int barheight;
     53 
     54 Atom atoms[ATOM_COUNT];
     55 
     56 void win_init_font(const win_env_t *e, const char *fontstr)
     57 {
     58 	if ((font = XftFontOpenName(e->dpy, e->scr, fontstr)) == NULL)
     59 		error(EXIT_FAILURE, 0, "Error loading font '%s'", fontstr);
     60 	fontheight = font->ascent + font->descent;
     61 	FcPatternGetDouble(font->pattern, FC_SIZE, 0, &fontsize);
     62 	barheight = fontheight + 2 * V_TEXT_PAD;
     63 }
     64 
     65 void win_alloc_color(const win_env_t *e, const char *name, XftColor *col)
     66 {
     67 	if (!XftColorAllocName(e->dpy, DefaultVisual(e->dpy, e->scr),
     68 	                       DefaultColormap(e->dpy, e->scr), name, col))
     69 	{
     70 		error(EXIT_FAILURE, 0, "Error allocating color '%s'", name);
     71 	}
     72 }
     73 
     74 const char* win_res(XrmDatabase db, const char *name, const char *def)
     75 {
     76 	char *type;
     77 	XrmValue ret;
     78 
     79 	if (db != None &&
     80 	    XrmGetResource(db, name, name, &type, &ret) &&
     81 	    STREQ(type, "String"))
     82 	{
     83 		return ret.addr;
     84 	} else {
     85 		return def;
     86 	}
     87 }
     88 
     89 #define INIT_ATOM_(atom) \
     90 	atoms[ATOM_##atom] = XInternAtom(e->dpy, #atom, False);
     91 
     92 void win_init(win_t *win)
     93 {
     94 	win_env_t *e;
     95 	const char *bg, *fg, *f;
     96 	char *res_man;
     97 	XrmDatabase db;
     98 
     99 	memset(win, 0, sizeof(win_t));
    100 
    101 	e = &win->env;
    102 	if ((e->dpy = XOpenDisplay(NULL)) == NULL)
    103 		error(EXIT_FAILURE, 0, "Error opening X display");
    104 
    105 	e->scr = DefaultScreen(e->dpy);
    106 	e->scrw = DisplayWidth(e->dpy, e->scr);
    107 	e->scrh = DisplayHeight(e->dpy, e->scr);
    108 	e->vis = DefaultVisual(e->dpy, e->scr);
    109 	e->cmap = DefaultColormap(e->dpy, e->scr);
    110 	e->depth = DefaultDepth(e->dpy, e->scr);
    111 
    112 	if (setlocale(LC_CTYPE, "") == NULL || XSupportsLocale() == 0)
    113 		error(0, 0, "No locale support");
    114 
    115 	XrmInitialize();
    116 	res_man = XResourceManagerString(e->dpy);
    117 	db = res_man != NULL ? XrmGetStringDatabase(res_man) : None;
    118 
    119 	f = win_res(db, RES_CLASS ".font", "monospace-8");
    120 	win_init_font(e, f);
    121 
    122 	bg = win_res(db, RES_CLASS ".background", "white");
    123 	fg = win_res(db, RES_CLASS ".foreground", "black");
    124 	win_alloc_color(e, bg, &win->bg);
    125 	win_alloc_color(e, fg, &win->fg);
    126 
    127 	win->bar.l.size = BAR_L_LEN;
    128 	win->bar.r.size = BAR_R_LEN;
    129 	/* 3 padding bytes needed by utf8_decode */
    130 	win->bar.l.buf = emalloc(win->bar.l.size + 3);
    131 	win->bar.l.buf[0] = '\0';
    132 	win->bar.r.buf = emalloc(win->bar.r.size + 3);
    133 	win->bar.r.buf[0] = '\0';
    134 	win->bar.h = options->hide_bar ? 0 : barheight;
    135 
    136 	INIT_ATOM_(WM_DELETE_WINDOW);
    137 	INIT_ATOM_(_NET_WM_NAME);
    138 	INIT_ATOM_(_NET_WM_ICON_NAME);
    139 	INIT_ATOM_(_NET_WM_ICON);
    140 	INIT_ATOM_(_NET_WM_STATE);
    141 	INIT_ATOM_(_NET_WM_STATE_FULLSCREEN);
    142 }
    143 
    144 void win_open(win_t *win)
    145 {
    146 	int c, i, j, n;
    147 	long parent;
    148 	win_env_t *e;
    149 	XClassHint classhint;
    150 	unsigned long *icon_data;
    151 	XColor col;
    152 	Cursor *cnone = &cursors[CURSOR_NONE].icon;
    153 	char none_data[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
    154 	Pixmap none;
    155 	int gmask;
    156 	XSizeHints sizehints;
    157 
    158 	e = &win->env;
    159 	parent = options->embed != 0 ? options->embed : RootWindow(e->dpy, e->scr);
    160 
    161 	sizehints.flags = PWinGravity;
    162 	sizehints.win_gravity = NorthWestGravity;
    163 
    164 	/* determine window offsets, width & height */
    165 	if (options->geometry == NULL)
    166 		gmask = 0;
    167 	else
    168 		gmask = XParseGeometry(options->geometry, &win->x, &win->y,
    169 		                       &win->w, &win->h);
    170 	if ((gmask & WidthValue) != 0)
    171 		sizehints.flags |= USSize;
    172 	else
    173 		win->w = WIN_WIDTH;
    174 	if ((gmask & HeightValue) != 0)
    175 		sizehints.flags |= USSize;
    176 	else
    177 		win->h = WIN_HEIGHT;
    178 	if ((gmask & XValue) != 0) {
    179 		if ((gmask & XNegative) != 0) {
    180 			win->x += e->scrw - win->w;
    181 			sizehints.win_gravity = NorthEastGravity;
    182 		}
    183 		sizehints.flags |= USPosition;
    184 	} else {
    185 		win->x = 0;
    186 	}
    187 	if ((gmask & YValue) != 0) {
    188 		if ((gmask & YNegative) != 0) {
    189 			win->y += e->scrh - win->h;
    190 			sizehints.win_gravity = sizehints.win_gravity == NorthEastGravity
    191 			                      ? SouthEastGravity : SouthWestGravity;
    192 		}
    193 		sizehints.flags |= USPosition;
    194 	} else {
    195 		win->y = 0;
    196 	}
    197 
    198 	win->xwin = XCreateWindow(e->dpy, parent,
    199 	                          win->x, win->y, win->w, win->h, 0,
    200 	                          e->depth, InputOutput, e->vis, 0, NULL);
    201 	if (win->xwin == None)
    202 		error(EXIT_FAILURE, 0, "Error creating X window");
    203 
    204 	XSelectInput(e->dpy, win->xwin,
    205 	             ButtonReleaseMask | ButtonPressMask | KeyPressMask |
    206 	             PointerMotionMask | StructureNotifyMask);
    207 
    208 	for (i = 0; i < ARRLEN(cursors); i++) {
    209 		if (i != CURSOR_NONE)
    210 			cursors[i].icon = XCreateFontCursor(e->dpy, cursors[i].name);
    211 	}
    212 	if (XAllocNamedColor(e->dpy, DefaultColormap(e->dpy, e->scr), "black",
    213 	                     &col, &col) == 0)
    214 	{
    215 		error(EXIT_FAILURE, 0, "Error allocating color 'black'");
    216 	}
    217 	none = XCreateBitmapFromData(e->dpy, win->xwin, none_data, 8, 8);
    218 	*cnone = XCreatePixmapCursor(e->dpy, none, none, &col, &col, 0, 0);
    219 
    220 	gc = XCreateGC(e->dpy, win->xwin, 0, None);
    221 
    222 	n = icons[ARRLEN(icons)-1].size;
    223 	icon_data = emalloc((n * n + 2) * sizeof(*icon_data));
    224 
    225 	for (i = 0; i < ARRLEN(icons); i++) {
    226 		n = 0;
    227 		icon_data[n++] = icons[i].size;
    228 		icon_data[n++] = icons[i].size;
    229 
    230 		for (j = 0; j < icons[i].cnt; j++) {
    231 			for (c = icons[i].data[j] >> 4; c >= 0; c--)
    232 				icon_data[n++] = icon_colors[icons[i].data[j] & 0x0F];
    233 		}
    234 		XChangeProperty(e->dpy, win->xwin,
    235 		                atoms[ATOM__NET_WM_ICON], XA_CARDINAL, 32,
    236 		                i == 0 ? PropModeReplace : PropModeAppend,
    237 		                (unsigned char *) icon_data, n);
    238 	}
    239 	free(icon_data);
    240 
    241 	win_set_title(win, "sxiv");
    242 
    243 	classhint.res_class = RES_CLASS;
    244 	classhint.res_name = options->res_name != NULL ? options->res_name : "sxiv";
    245 	XSetClassHint(e->dpy, win->xwin, &classhint);
    246 
    247 	XSetWMProtocols(e->dpy, win->xwin, &atoms[ATOM_WM_DELETE_WINDOW], 1);
    248 
    249 	sizehints.width = win->w;
    250 	sizehints.height = win->h;
    251 	sizehints.x = win->x;
    252 	sizehints.y = win->y;
    253 	XSetWMNormalHints(win->env.dpy, win->xwin, &sizehints);
    254 
    255 	win->h -= win->bar.h;
    256 
    257 	win->buf.w = e->scrw;
    258 	win->buf.h = e->scrh;
    259 	win->buf.pm = XCreatePixmap(e->dpy, win->xwin,
    260 	                            win->buf.w, win->buf.h, e->depth);
    261 	XSetForeground(e->dpy, gc, win->bg.pixel);
    262 	XFillRectangle(e->dpy, win->buf.pm, gc, 0, 0, win->buf.w, win->buf.h);
    263 	XSetWindowBackgroundPixmap(e->dpy, win->xwin, win->buf.pm);
    264 
    265 	XMapWindow(e->dpy, win->xwin);
    266 	XFlush(e->dpy);
    267 
    268 	if (options->fullscreen)
    269 		win_toggle_fullscreen(win);
    270 }
    271 
    272 CLEANUP void win_close(win_t *win)
    273 {
    274 	int i;
    275 
    276 	for (i = 0; i < ARRLEN(cursors); i++)
    277 		XFreeCursor(win->env.dpy, cursors[i].icon);
    278 
    279 	XFreeGC(win->env.dpy, gc);
    280 
    281 	XDestroyWindow(win->env.dpy, win->xwin);
    282 	XCloseDisplay(win->env.dpy);
    283 }
    284 
    285 bool win_configure(win_t *win, XConfigureEvent *c)
    286 {
    287 	bool changed;
    288 
    289 	changed = win->w != c->width || win->h + win->bar.h != c->height;
    290 
    291 	win->x = c->x;
    292 	win->y = c->y;
    293 	win->w = c->width;
    294 	win->h = c->height - win->bar.h;
    295 	win->bw = c->border_width;
    296 
    297 	return changed;
    298 }
    299 
    300 void win_toggle_fullscreen(win_t *win)
    301 {
    302 	XEvent ev;
    303 	XClientMessageEvent *cm;
    304 
    305 	memset(&ev, 0, sizeof(ev));
    306 	ev.type = ClientMessage;
    307 
    308 	cm = &ev.xclient;
    309 	cm->window = win->xwin;
    310 	cm->message_type = atoms[ATOM__NET_WM_STATE];
    311 	cm->format = 32;
    312 	cm->data.l[0] = 2; // toggle
    313 	cm->data.l[1] = atoms[ATOM__NET_WM_STATE_FULLSCREEN];
    314 
    315 	XSendEvent(win->env.dpy, DefaultRootWindow(win->env.dpy), False,
    316 	           SubstructureNotifyMask | SubstructureRedirectMask, &ev);
    317 }
    318 
    319 void win_toggle_bar(win_t *win)
    320 {
    321 	if (win->bar.h != 0) {
    322 		win->h += win->bar.h;
    323 		win->bar.h = 0;
    324 	} else {
    325 		win->bar.h = barheight;
    326 		win->h -= win->bar.h;
    327 	}
    328 }
    329 
    330 void win_clear(win_t *win)
    331 {
    332 	win_env_t *e;
    333 
    334 	e = &win->env;
    335 
    336 	if (win->w > win->buf.w || win->h + win->bar.h > win->buf.h) {
    337 		XFreePixmap(e->dpy, win->buf.pm);
    338 		win->buf.w = MAX(win->buf.w, win->w);
    339 		win->buf.h = MAX(win->buf.h, win->h + win->bar.h);
    340 		win->buf.pm = XCreatePixmap(e->dpy, win->xwin,
    341 		                            win->buf.w, win->buf.h, e->depth);
    342 	}
    343 	XSetForeground(e->dpy, gc, win->bg.pixel);
    344 	XFillRectangle(e->dpy, win->buf.pm, gc, 0, 0, win->buf.w, win->buf.h);
    345 }
    346 
    347 #define TEXTWIDTH(win, text, len) \
    348 	win_draw_text(win, NULL, NULL, 0, 0, text, len, 0)
    349 
    350 int win_draw_text(win_t *win, XftDraw *d, const XftColor *color, int x, int y,
    351                   char *text, int len, int w)
    352 {
    353 	int err, tw = 0;
    354 	char *t, *next;
    355 	uint32_t rune;
    356 	XftFont *f;
    357 	FcCharSet *fccharset;
    358 	XGlyphInfo ext;
    359 
    360 	for (t = text; t - text < len; t = next) {
    361 		next = utf8_decode(t, &rune, &err);
    362 		if (XftCharExists(win->env.dpy, font, rune)) {
    363 			f = font;
    364 		} else { /* fallback font */
    365 			fccharset = FcCharSetCreate();
    366 			FcCharSetAddChar(fccharset, rune);
    367 			f = XftFontOpen(win->env.dpy, win->env.scr, FC_CHARSET, FcTypeCharSet,
    368 			                fccharset, FC_SCALABLE, FcTypeBool, FcTrue,
    369 			                FC_SIZE, FcTypeDouble, fontsize, NULL);
    370 			FcCharSetDestroy(fccharset);
    371 		}
    372 		XftTextExtentsUtf8(win->env.dpy, f, (XftChar8*)t, next - t, &ext);
    373 		tw += ext.xOff;
    374 		if (tw <= w) {
    375 			XftDrawStringUtf8(d, color, f, x, y, (XftChar8*)t, next - t);
    376 			x += ext.xOff;
    377 		}
    378 		if (f != font)
    379 			XftFontClose(win->env.dpy, f);
    380 	}
    381 	return tw;
    382 }
    383 
    384 void win_draw_bar(win_t *win)
    385 {
    386 	int len, x, y, w, tw;
    387 	win_env_t *e;
    388 	win_bar_t *l, *r;
    389 	XftDraw *d;
    390 
    391 	if ((l = &win->bar.l)->buf == NULL || (r = &win->bar.r)->buf == NULL)
    392 		return;
    393 
    394 	e = &win->env;
    395 	y = win->h + font->ascent + V_TEXT_PAD;
    396 	w = win->w - 2*H_TEXT_PAD;
    397 	d = XftDrawCreate(e->dpy, win->buf.pm, DefaultVisual(e->dpy, e->scr),
    398 	                  DefaultColormap(e->dpy, e->scr));
    399 
    400 	XSetForeground(e->dpy, gc, win->bg.pixel);
    401 	XFillRectangle(e->dpy, win->buf.pm, gc, 0, win->h, win->w, win->bar.h);
    402 
    403 	XSetForeground(e->dpy, gc, win->fg.pixel);
    404 	XSetBackground(e->dpy, gc, win->bg.pixel);
    405 
    406 	if ((len = strlen(r->buf)) > 0) {
    407 		if ((tw = TEXTWIDTH(win, r->buf, len)) > w)
    408 			return;
    409 		x = win->w - tw - H_TEXT_PAD;
    410 		w -= tw;
    411 		win_draw_text(win, d, &win->fg, x, y, r->buf, len, tw);
    412 	}
    413 	if ((len = strlen(l->buf)) > 0) {
    414 		x = H_TEXT_PAD;
    415 		w -= 2 * H_TEXT_PAD; /* gap between left and right parts */
    416 		win_draw_text(win, d, &win->fg, x, y, l->buf, len, w);
    417 	}
    418 	XftDrawDestroy(d);
    419 }
    420 
    421 void win_draw(win_t *win)
    422 {
    423 	if (win->bar.h > 0)
    424 		win_draw_bar(win);
    425 
    426 	XSetWindowBackgroundPixmap(win->env.dpy, win->xwin, win->buf.pm);
    427 	XClearWindow(win->env.dpy, win->xwin);
    428 	XFlush(win->env.dpy);
    429 }
    430 
    431 void win_draw_rect(win_t *win, int x, int y, int w, int h, bool fill, int lw,
    432                    unsigned long col)
    433 {
    434 	XGCValues gcval;
    435 
    436 	gcval.line_width = lw;
    437 	gcval.foreground = col;
    438 	XChangeGC(win->env.dpy, gc, GCForeground | GCLineWidth, &gcval);
    439 
    440 	if (fill)
    441 		XFillRectangle(win->env.dpy, win->buf.pm, gc, x, y, w, h);
    442 	else
    443 		XDrawRectangle(win->env.dpy, win->buf.pm, gc, x, y, w, h);
    444 }
    445 
    446 void win_set_title(win_t *win, const char *title)
    447 {
    448 	XStoreName(win->env.dpy, win->xwin, title);
    449 	XSetIconName(win->env.dpy, win->xwin, title);
    450 
    451 	XChangeProperty(win->env.dpy, win->xwin, atoms[ATOM__NET_WM_NAME],
    452 	                XInternAtom(win->env.dpy, "UTF8_STRING", False), 8,
    453 	                PropModeReplace, (unsigned char *) title, strlen(title));
    454 	XChangeProperty(win->env.dpy, win->xwin, atoms[ATOM__NET_WM_ICON_NAME],
    455 	                XInternAtom(win->env.dpy, "UTF8_STRING", False), 8,
    456 	                PropModeReplace, (unsigned char *) title, strlen(title));
    457 }
    458 
    459 void win_set_cursor(win_t *win, cursor_t cursor)
    460 {
    461 	if (cursor >= 0 && cursor < ARRLEN(cursors)) {
    462 		XDefineCursor(win->env.dpy, win->xwin, cursors[cursor].icon);
    463 		XFlush(win->env.dpy);
    464 	}
    465 }
    466 
    467 void win_cursor_pos(win_t *win, int *x, int *y)
    468 {
    469 	int i;
    470 	unsigned int ui;
    471 	Window w;
    472 
    473 	if (!XQueryPointer(win->env.dpy, win->xwin, &w, &w, &i, &i, x, y, &ui))
    474 		*x = *y = 0;
    475 }
    476