Krolyxon
Commit f7fc663

Branches

Refs →

nsxivwindow.c

522 lines · 13.6 KB · C

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
34enum {
35 H_TEXT_PAD = 5,
36 V_TEXT_PAD = 1
37};
38
39static 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
47static GC gc;
48
49static XftFont *font;
50static int fontheight;
51static int barheight;
52
53Atom atoms[ATOM_COUNT];
54
55static Bool fs_support;
56static Bool fs_warned;
57
58void win_init_font(const win_env_t *e, const char *fontstr)
59{
60 if ((font = XftFontOpenName(e->dpy, e->scr, fontstr)) == NULL)
61 error(EXIT_FAILURE, 0, "Error loading font '%s'", fontstr);
62 fontheight = font->ascent + font->descent;
63 barheight = fontheight + 2 * V_TEXT_PAD;
64}
65
66void win_alloc_color(const win_env_t *e, const char *name, XftColor *col)
67{
68 if (!XftColorAllocName(e->dpy, DefaultVisual(e->dpy, e->scr),
69 DefaultColormap(e->dpy, e->scr), name, col))
70 {
71 error(EXIT_FAILURE, 0, "Error allocating color '%s'", name);
72 }
73}
74
75void win_check_wm_support(Display *dpy, Window root)
76{
77 int format;
78 long offset = 0, length = 16;
79 Atom *data, type;
80 unsigned long i, nitems, bytes_left;
81 Bool found = False;
82
83 while (!found && length > 0) {
84 if (XGetWindowProperty(dpy, root, atoms[ATOM__NET_SUPPORTED],
85 offset, length, False, XA_ATOM, &type, &format,
86 &nitems, &bytes_left, (unsigned char**) &data))
87 {
88 break;
89 }
90 if (type == XA_ATOM && format == 32) {
91 for (i = 0; i < nitems; i++) {
92 if (data[i] == atoms[ATOM__NET_WM_STATE_FULLSCREEN]) {
93 found = True;
94 fs_support = True;
95 break;
96 }
97 }
98 }
99 XFree(data);
100 offset += nitems;
101 length = MIN(length, bytes_left / 4);
102 }
103}
104
105void win_res(Display *dpy, const char *name, const char **dst)
106{
107 char *type;
108 XrmValue ret;
109 XrmDatabase db;
110 char *res_man;
111
112 XrmInitialize();
113
114 if ((res_man = XResourceManagerString(dpy)) == NULL)
115 return;
116
117 if ((db = XrmGetStringDatabase(res_man)) == NULL)
118 return;
119
120 if (XrmGetResource(db, name, name, &type, &ret) && STREQ(type, "String"))
121 *dst = ret.addr;
122}
123
124#define INIT_ATOM_(atom) \
125 atoms[ATOM_##atom] = XInternAtom(e->dpy, #atom, False);
126
127void win_init(win_t *win)
128{
129 win_env_t *e;
130
131 memset(win, 0, sizeof(win_t));
132
133 e = &win->env;
134 if ((e->dpy = XOpenDisplay(NULL)) == NULL)
135 error(EXIT_FAILURE, 0, "Error opening X display");
136
137 e->scr = DefaultScreen(e->dpy);
138 e->scrw = DisplayWidth(e->dpy, e->scr);
139 e->scrh = DisplayHeight(e->dpy, e->scr);
140 e->vis = DefaultVisual(e->dpy, e->scr);
141 e->cmap = DefaultColormap(e->dpy, e->scr);
142 e->depth = DefaultDepth(e->dpy, e->scr);
143
144 if (setlocale(LC_CTYPE, "") == NULL || XSupportsLocale() == 0)
145 error(0, 0, "No locale support");
146
147 win_res(e->dpy, RES_CLASS ".background", &WIN_BG_COLOR);
148 win_res(e->dpy, RES_CLASS ".background", &BAR_FG_COLOR);
149 win_res(e->dpy, RES_CLASS ".foreground", &BAR_BG_COLOR);
150 win_res(e->dpy, RES_CLASS ".foreground", &SEL_COLOR);
151
152 win_init_font(e, BAR_FONT);
153
154 win_alloc_color(e, WIN_BG_COLOR, &win->bgcol);
155 win_alloc_color(e, WIN_FS_COLOR, &win->fscol);
156 win_alloc_color(e, SEL_COLOR, &win->selcol);
157 win_alloc_color(e, BAR_BG_COLOR, &win->bar.bgcol);
158 win_alloc_color(e, BAR_FG_COLOR, &win->bar.fgcol);
159
160 win->bar.l.size = BAR_L_LEN;
161 win->bar.r.size = BAR_R_LEN;
162 /* 3 padding bytes needed by utf8_decode */
163 win->bar.l.buf = emalloc(win->bar.l.size + 3);
164 win->bar.l.buf[0] = '\0';
165 win->bar.r.buf = emalloc(win->bar.r.size + 3);
166 win->bar.r.buf[0] = '\0';
167 win->bar.h = options->hide_bar ? 0 : barheight;
168
169 INIT_ATOM_(WM_DELETE_WINDOW);
170 INIT_ATOM_(_NET_WM_NAME);
171 INIT_ATOM_(_NET_WM_ICON_NAME);
172 INIT_ATOM_(_NET_WM_ICON);
173 INIT_ATOM_(_NET_WM_STATE);
174 INIT_ATOM_(_NET_WM_STATE_FULLSCREEN);
175 INIT_ATOM_(_NET_SUPPORTED);
176
177 win_check_wm_support(e->dpy, RootWindow(e->dpy, e->scr));
178}
179
180void win_open(win_t *win)
181{
182 int c, i, j, n;
183 long parent;
184 win_env_t *e;
185 XClassHint classhint;
186 unsigned long *icon_data;
187 XColor col;
188 Cursor *cnone = &cursors[CURSOR_NONE].icon;
189 char none_data[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
190 Pixmap none;
191 int gmask;
192 XSizeHints sizehints;
193 Bool fullscreen = options->fullscreen && fs_support;
194
195 e = &win->env;
196 parent = options->embed != 0 ? options->embed : RootWindow(e->dpy, e->scr);
197
198 sizehints.flags = PWinGravity;
199 sizehints.win_gravity = NorthWestGravity;
200
201 /* determine window offsets, width & height */
202 if (options->geometry == NULL)
203 gmask = 0;
204 else
205 gmask = XParseGeometry(options->geometry, &win->x, &win->y,
206 &win->w, &win->h);
207 if ((gmask & WidthValue) != 0)
208 sizehints.flags |= USSize;
209 else
210 win->w = WIN_WIDTH;
211 if ((gmask & HeightValue) != 0)
212 sizehints.flags |= USSize;
213 else
214 win->h = WIN_HEIGHT;
215 if ((gmask & XValue) != 0) {
216 if ((gmask & XNegative) != 0) {
217 win->x += e->scrw - win->w;
218 sizehints.win_gravity = NorthEastGravity;
219 }
220 sizehints.flags |= USPosition;
221 } else {
222 win->x = 0;
223 }
224 if ((gmask & YValue) != 0) {
225 if ((gmask & YNegative) != 0) {
226 win->y += e->scrh - win->h;
227 sizehints.win_gravity = sizehints.win_gravity == NorthEastGravity
228 ? SouthEastGravity : SouthWestGravity;
229 }
230 sizehints.flags |= USPosition;
231 } else {
232 win->y = 0;
233 }
234
235 win->xwin = XCreateWindow(e->dpy, parent,
236 win->x, win->y, win->w, win->h, 0,
237 e->depth, InputOutput, e->vis, 0, NULL);
238 if (win->xwin == None)
239 error(EXIT_FAILURE, 0, "Error creating X window");
240
241 XSelectInput(e->dpy, win->xwin,
242 ButtonReleaseMask | ButtonPressMask | KeyPressMask |
243 PointerMotionMask | StructureNotifyMask);
244
245 for (i = 0; i < ARRLEN(cursors); i++) {
246 if (i != CURSOR_NONE)
247 cursors[i].icon = XCreateFontCursor(e->dpy, cursors[i].name);
248 }
249 if (XAllocNamedColor(e->dpy, DefaultColormap(e->dpy, e->scr), "black",
250 &col, &col) == 0)
251 {
252 error(EXIT_FAILURE, 0, "Error allocating color 'black'");
253 }
254 none = XCreateBitmapFromData(e->dpy, win->xwin, none_data, 8, 8);
255 *cnone = XCreatePixmapCursor(e->dpy, none, none, &col, &col, 0, 0);
256
257 gc = XCreateGC(e->dpy, win->xwin, 0, None);
258
259 n = icons[ARRLEN(icons)-1].size;
260 icon_data = emalloc((n * n + 2) * sizeof(*icon_data));
261
262 for (i = 0; i < ARRLEN(icons); i++) {
263 n = 0;
264 icon_data[n++] = icons[i].size;
265 icon_data[n++] = icons[i].size;
266
267 for (j = 0; j < icons[i].cnt; j++) {
268 for (c = icons[i].data[j] >> 4; c >= 0; c--)
269 icon_data[n++] = icon_colors[icons[i].data[j] & 0x0F];
270 }
271 XChangeProperty(e->dpy, win->xwin,
272 atoms[ATOM__NET_WM_ICON], XA_CARDINAL, 32,
273 i == 0 ? PropModeReplace : PropModeAppend,
274 (unsigned char *) icon_data, n);
275 }
276 free(icon_data);
277
278 win_set_title(win, "sxiv");
279
280 classhint.res_class = RES_CLASS;
281 classhint.res_name = options->res_name != NULL ? options->res_name : "sxiv";
282 XSetClassHint(e->dpy, win->xwin, &classhint);
283
284 XSetWMProtocols(e->dpy, win->xwin, &atoms[ATOM_WM_DELETE_WINDOW], 1);
285
286 sizehints.width = win->w;
287 sizehints.height = win->h;
288 sizehints.x = win->x;
289 sizehints.y = win->y;
290 XSetWMNormalHints(win->env.dpy, win->xwin, &sizehints);
291
292 win->h -= win->bar.h;
293
294 win->buf.w = e->scrw;
295 win->buf.h = e->scrh;
296 win->buf.pm = XCreatePixmap(e->dpy, win->xwin,
297 win->buf.w, win->buf.h, e->depth);
298 XSetForeground(e->dpy, gc, fullscreen ? win->fscol.pixel : win->bgcol.pixel);
299 XFillRectangle(e->dpy, win->buf.pm, gc, 0, 0, win->buf.w, win->buf.h);
300 XSetWindowBackgroundPixmap(e->dpy, win->xwin, win->buf.pm);
301
302 XMapWindow(e->dpy, win->xwin);
303 XFlush(e->dpy);
304
305 if (fullscreen)
306 win_toggle_fullscreen(win);
307}
308
309CLEANUP void win_close(win_t *win)
310{
311 int i;
312
313 for (i = 0; i < ARRLEN(cursors); i++)
314 XFreeCursor(win->env.dpy, cursors[i].icon);
315
316 XFreeGC(win->env.dpy, gc);
317
318 XDestroyWindow(win->env.dpy, win->xwin);
319 XCloseDisplay(win->env.dpy);
320}
321
322bool win_configure(win_t *win, XConfigureEvent *c)
323{
324 bool changed;
325
326 changed = win->w != c->width || win->h + win->bar.h != c->height;
327
328 win->x = c->x;
329 win->y = c->y;
330 win->w = c->width;
331 win->h = c->height - win->bar.h;
332 win->bw = c->border_width;
333
334 return changed;
335}
336
337void win_toggle_fullscreen(win_t *win)
338{
339 XEvent ev;
340 XClientMessageEvent *cm;
341
342 if (!fs_support) {
343 if (!fs_warned) {
344 error(0, 0, "No fullscreen support");
345 fs_warned = True;
346 }
347 return;
348 }
349 win->fullscreen = !win->fullscreen;
350
351 memset(&ev, 0, sizeof(ev));
352 ev.type = ClientMessage;
353
354 cm = &ev.xclient;
355 cm->window = win->xwin;
356 cm->message_type = atoms[ATOM__NET_WM_STATE];
357 cm->format = 32;
358 cm->data.l[0] = win->fullscreen;
359 cm->data.l[1] = atoms[ATOM__NET_WM_STATE_FULLSCREEN];
360 cm->data.l[2] = cm->data.l[3] = 0;
361
362 XSendEvent(win->env.dpy, DefaultRootWindow(win->env.dpy), False,
363 SubstructureNotifyMask | SubstructureRedirectMask, &ev);
364}
365
366void win_toggle_bar(win_t *win)
367{
368 if (win->bar.h != 0) {
369 win->h += win->bar.h;
370 win->bar.h = 0;
371 } else {
372 win->bar.h = barheight;
373 win->h -= win->bar.h;
374 }
375}
376
377void win_clear(win_t *win)
378{
379 win_env_t *e;
380
381 e = &win->env;
382
383 if (win->w > win->buf.w || win->h + win->bar.h > win->buf.h) {
384 XFreePixmap(e->dpy, win->buf.pm);
385 win->buf.w = MAX(win->buf.w, win->w);
386 win->buf.h = MAX(win->buf.h, win->h + win->bar.h);
387 win->buf.pm = XCreatePixmap(e->dpy, win->xwin,
388 win->buf.w, win->buf.h, e->depth);
389 }
390 XSetForeground(e->dpy, gc, win->fullscreen ? win->fscol.pixel : win->bgcol.pixel);
391 XFillRectangle(e->dpy, win->buf.pm, gc, 0, 0, win->buf.w, win->buf.h);
392}
393
394#define TEXTWIDTH(win, text, len) \
395 win_draw_text(win, NULL, NULL, 0, 0, text, len, 0)
396
397int win_draw_text(win_t *win, XftDraw *d, XftColor *color, int x, int y,
398 char *text, int len, int w)
399{
400 int err, tw = 0;
401 char *t, *next;
402 uint32_t rune;
403 XftFont *f;
404 FcCharSet *fccharset;
405 XGlyphInfo ext;
406
407 for (t = text; t - text < len; t = next) {
408 next = utf8_decode(t, &rune, &err);
409 if (XftCharExists(win->env.dpy, font, rune)) {
410 f = font;
411 } else { /* fallback font */
412 fccharset = FcCharSetCreate();
413 FcCharSetAddChar(fccharset, rune);
414 f = XftFontOpen(win->env.dpy, win->env.scr, FC_CHARSET, FcTypeCharSet,
415 fccharset, FC_SCALABLE, FcTypeBool, FcTrue, NULL);
416 FcCharSetDestroy(fccharset);
417 }
418 XftTextExtentsUtf8(win->env.dpy, f, (XftChar8*)t, next - t, &ext);
419 tw += ext.xOff;
420 if (tw <= w) {
421 XftDrawStringUtf8(d, color, f, x, y, (XftChar8*)t, next - t);
422 x += ext.xOff;
423 }
424 if (f != font)
425 XftFontClose(win->env.dpy, f);
426 }
427 return tw;
428}
429
430void win_draw_bar(win_t *win)
431{
432 int len, x, y, w, tw;
433 win_env_t *e;
434 win_bar_t *l, *r;
435 XftDraw *d;
436
437 if ((l = &win->bar.l)->buf == NULL || (r = &win->bar.r)->buf == NULL)
438 return;
439
440 e = &win->env;
441 y = win->h + font->ascent + V_TEXT_PAD;
442 w = win->w - 2*H_TEXT_PAD;
443 d = XftDrawCreate(e->dpy, win->buf.pm, DefaultVisual(e->dpy, e->scr),
444 DefaultColormap(e->dpy, e->scr));
445
446 XSetForeground(e->dpy, gc, win->bar.bgcol.pixel);
447 XFillRectangle(e->dpy, win->buf.pm, gc, 0, win->h, win->w, win->bar.h);
448
449 XSetForeground(e->dpy, gc, win->bar.fgcol.pixel);
450 XSetBackground(e->dpy, gc, win->bar.bgcol.pixel);
451
452 if ((len = strlen(r->buf)) > 0) {
453 if ((tw = TEXTWIDTH(win, r->buf, len)) > w)
454 return;
455 x = win->w - tw - H_TEXT_PAD;
456 w -= tw;
457 win_draw_text(win, d, &win->bar.fgcol, x, y, r->buf, len, tw);
458 }
459 if ((len = strlen(l->buf)) > 0) {
460 x = H_TEXT_PAD;
461 w -= 2 * H_TEXT_PAD; /* gap between left and right parts */
462 win_draw_text(win, d, &win->bar.fgcol, x, y, l->buf, len, w);
463 }
464 XftDrawDestroy(d);
465}
466
467void win_draw(win_t *win)
468{
469 if (win->bar.h > 0)
470 win_draw_bar(win);
471
472 XSetWindowBackgroundPixmap(win->env.dpy, win->xwin, win->buf.pm);
473 XClearWindow(win->env.dpy, win->xwin);
474 XFlush(win->env.dpy);
475}
476
477void win_draw_rect(win_t *win, int x, int y, int w, int h, bool fill, int lw,
478 unsigned long col)
479{
480 XGCValues gcval;
481
482 gcval.line_width = lw;
483 gcval.foreground = col;
484 XChangeGC(win->env.dpy, gc, GCForeground | GCLineWidth, &gcval);
485
486 if (fill)
487 XFillRectangle(win->env.dpy, win->buf.pm, gc, x, y, w, h);
488 else
489 XDrawRectangle(win->env.dpy, win->buf.pm, gc, x, y, w, h);
490}
491
492void win_set_title(win_t *win, const char *title)
493{
494 XStoreName(win->env.dpy, win->xwin, title);
495 XSetIconName(win->env.dpy, win->xwin, title);
496
497 XChangeProperty(win->env.dpy, win->xwin, atoms[ATOM__NET_WM_NAME],
498 XInternAtom(win->env.dpy, "UTF8_STRING", False), 8,
499 PropModeReplace, (unsigned char *) title, strlen(title));
500 XChangeProperty(win->env.dpy, win->xwin, atoms[ATOM__NET_WM_ICON_NAME],
501 XInternAtom(win->env.dpy, "UTF8_STRING", False), 8,
502 PropModeReplace, (unsigned char *) title, strlen(title));
503}
504
505void win_set_cursor(win_t *win, cursor_t cursor)
506{
507 if (cursor >= 0 && cursor < ARRLEN(cursors)) {
508 XDefineCursor(win->env.dpy, win->xwin, cursors[cursor].icon);
509 XFlush(win->env.dpy);
510 }
511}
512
513void win_cursor_pos(win_t *win, int *x, int *y)
514{
515 int i;
516 unsigned int ui;
517 Window w;
518
519 if (!XQueryPointer(win->env.dpy, win->xwin, &w, &w, &i, &i, x, y, &ui))
520 *x = *y = 0;
521}
522