Skip to content

Instantly share code, notes, and snippets.

@richardgv
Last active December 22, 2015 02:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save richardgv/6404725 to your computer and use it in GitHub Desktop.
Save richardgv/6404725 to your computer and use it in GitHub Desktop.
richardgv/skippy-xd: Work in progress (icon rendering)
diff --git a/Makefile b/Makefile
index 12fbe8e..81d25db 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ BINDIR ?= ${PREFIX}/bin
CC ?= gcc
-SRCS_RAW = skippy wm dlist mainwin clientwin layout focus config tooltip img
+SRCS_RAW = skippy wm dlist mainwin clientwin layout focus config tooltip img img-xlib
PACKAGES = x11 xft xrender xcomposite xdamage xfixes
# === Options ===
diff --git a/skippy-xd.rc-default b/skippy-xd.rc-default
index ac0aa48..cb24ae8 100644
--- a/skippy-xd.rc-default
+++ b/skippy-xd.rc-default
@@ -48,8 +48,15 @@ movePointerOnStart = true
movePointerOnSelect = true
movePointerOnRaise = true
useNameWindowPixmap = false
+forceNameWindowPixmap = false
includeFrame = true
allowUpscale = true
+showAllDesktops = true
+showUnmapped = true
+preferredIconSize = 48
+clientDisplayModes = thumbnail icon filled none
+iconFillSpec = orig mid mid #00FFFF
+fillSpec = orig mid mid #FFFFFF
background =
[xinerama]
diff --git a/src/clientwin.c b/src/clientwin.c
index c4c1af1..377247f 100644
--- a/src/clientwin.c
+++ b/src/clientwin.c
@@ -37,19 +37,24 @@ int
clientwin_validate_func(dlist *l, void *data) {
ClientWin *cw = l->data;
MainWin *mw = cw->mainwin;
+ session_t *ps = mw->ps;
- CARD32 desktop = (*(CARD32*)data),
- w_desktop = wm_get_window_desktop(mw->ps, cw->wid_client);
-
#ifdef CFG_XINERAMA
- if(mw->xin_active && ! INTERSECTS(cw->src.x, cw->src.y, cw->src.width, cw->src.height,
- mw->xin_active->x_org, mw->xin_active->y_org,
- mw->xin_active->width, mw->xin_active->height))
- return 0;
+ if (mw->xin_active && !INTERSECTS(cw->src.x, cw->src.y, cw->src.width,
+ cw->src.height, mw->xin_active->x_org, mw->xin_active->y_org,
+ mw->xin_active->width, mw->xin_active->height))
+ return false;
#endif
-
- return (w_desktop == (CARD32)-1 || desktop == w_desktop) &&
- wm_validate_window(mw->ps, cw->wid_client);
+
+ if (!ps->o.showAllDesktops) {
+ CARD32 desktop = (*(CARD32 *)data),
+ w_desktop = wm_get_window_desktop(ps, cw->wid_client);
+
+ if (!(w_desktop == (CARD32) -1 || desktop == w_desktop))
+ return false;
+ }
+
+ return wm_validate_window(mw->ps, cw->wid_client);
}
int
@@ -70,14 +75,22 @@ clientwin_sort_func(dlist* a, dlist* b, void* data)
ClientWin *
clientwin_create(MainWin *mw, Window client) {
session_t *ps = mw->ps;
- ClientWin *cw = allocchk(malloc(sizeof(ClientWin)));
+ ClientWin *cw = NULL;
+
+ XWindowAttributes attr = { };
+ XGetWindowAttributes(ps->dpy, client, &attr);
+
+ // Check if window is mapped
+ // TODO: Move to validate function?
+ if (!ps->o.showUnmapped && IsViewable != attr.map_state)
+ goto clientwin_create_err;
+
+ cw = smalloc(1, ClientWin);
{
static const ClientWin CLIENTWT_DEF = CLIENTWT_INIT;
memcpy(cw, &CLIENTWT_DEF, sizeof(ClientWin));
}
- XWindowAttributes attr;
-
cw->mainwin = mw;
cw->wid_client = client;
if (ps->o.includeFrame)
@@ -102,7 +115,7 @@ clientwin_create(MainWin *mw, Window client) {
}
if (!cw->mini.window)
goto clientwin_create_err;
-
+
{
static const char *PREFIX = "mini window of ";
const int len = strlen(PREFIX) + 20;
@@ -116,31 +129,6 @@ clientwin_create(MainWin *mw, Window client) {
// this is to be done as early as possible
XSelectInput(cw->mainwin->ps->dpy, cw->src.window, SubstructureNotifyMask | StructureNotifyMask);
- XGetWindowAttributes(ps->dpy, client, &attr);
- if (IsViewable != attr.map_state)
- goto clientwin_create_err;
- clientwin_update(cw);
-
- // Get window pixmap
- if (ps->o.useNameWindowPixmap) {
- XCompositeRedirectWindow(ps->dpy, cw->src.window, CompositeRedirectAutomatic);
- cw->redirected = true;
- cw->cpixmap = XCompositeNameWindowPixmap(ps->dpy, cw->src.window);
- }
- // Create window picture
- {
- Drawable draw = cw->cpixmap;
- if (!draw) draw = cw->src.window;
- XRenderPictureAttributes pa = { .subwindow_mode = IncludeInferiors };
- cw->origin = XRenderCreatePicture(cw->mainwin->ps->dpy,
- draw, cw->src.format, CPSubwindowMode, &pa);
- }
- if (!cw->origin)
- goto clientwin_create_err;
-
- XRenderSetPictureFilter(cw->mainwin->ps->dpy, cw->origin, FilterBest, 0, 0);
-
-
return cw;
clientwin_create_err:
@@ -150,21 +138,101 @@ clientwin_create_err:
return NULL;
}
-void
+/**
+ * @brief Update window data to prepare for rendering.
+ */
+bool
clientwin_update(ClientWin *cw) {
- Window tmpwin;
- XWindowAttributes wattr;
+ MainWin *mw = cw->mainwin;
+ session_t *ps = mw->ps;
+
+ clientwin_free_res2(ps, cw);
+
+ // Get window attributes
+ XWindowAttributes wattr = { };
+ XGetWindowAttributes(ps->dpy, cw->src.window, &wattr);
+
+ {
+ {
+ Window tmpwin = None;
+ XTranslateCoordinates(ps->dpy, cw->src.window, wattr.root,
+ -wattr.border_width, -wattr.border_width,
+ &cw->src.x, &cw->src.y, &tmpwin);
+ }
+ cw->src.width = wattr.width;
+ cw->src.height = wattr.height;
+ cw->src.format = XRenderFindVisualFormat(ps->dpy, wattr.visual);
+ }
+
+ if (IsViewable == wattr.map_state) {
+ // Get window pixmap
+ if (ps->o.useNameWindowPixmap) {
+ XCompositeRedirectWindow(ps->dpy, cw->src.window,
+ CompositeRedirectAutomatic);
+ cw->redirected = true;
+ cw->cpixmap = XCompositeNameWindowPixmap(ps->dpy, cw->src.window);
+ }
+
+ // Create window picture
+ if (!(ps->o.useNameWindowPixmap && ps->o.forceNameWindowPixmap
+ && !cw->cpixmap)) {
+ Drawable draw = cw->cpixmap;
+ if (!draw) draw = cw->src.window;
+ static XRenderPictureAttributes pa = { .subwindow_mode = IncludeInferiors };
+ cw->origin = XRenderCreatePicture(ps->dpy,
+ draw, cw->src.format, CPSubwindowMode, &pa);
+ }
+ if (cw->origin) {
+ XRenderSetPictureFilter(ps->dpy, cw->origin, FilterBest, 0, 0);
+ }
+ }
- XGetWindowAttributes(cw->mainwin->ps->dpy, cw->src.window, &wattr);
- XTranslateCoordinates(cw->mainwin->ps->dpy, cw->src.window, wattr.root,
- -wattr.border_width, -wattr.border_width,
- &cw->src.x, &cw->src.y, &tmpwin);
- cw->src.width = wattr.width;
- cw->src.height = wattr.height;
- cw->src.format = XRenderFindVisualFormat(cw->mainwin->ps->dpy, wattr.visual);
+ // Get window icon
+ cw->icon_pict = simg_load_icon(ps, cw->wid_client, ps->o.preferredIconSize);
+ if (!cw->icon_pict && ps->o.iconDefault)
+ cw->icon_pict = clone_pictw(ps, ps->o.iconDefault);
+ // Reset mini window parameters
cw->mini.x = cw->mini.y = 0;
cw->mini.width = cw->mini.height = 1;
+
+ cw->mode = clientwin_get_disp_mode(ps, cw);
+ // printfdf("(%#010lx): %d", cw->wid_client, cw->mode);
+
+ return true;
+}
+
+bool
+clientwin_update2(ClientWin *cw) {
+ MainWin *mw = cw->mainwin;
+ session_t *ps = mw->ps;
+
+ clientwin_free_res2(ps, cw);
+
+ switch (cw->mode) {
+ case CLIDISP_NONE:
+ break;
+ case CLIDISP_FILLED:
+ cw->pict_filled = simg_postprocess(ps,
+ clone_pictw(ps, ps->o.fillSpec.img),
+ ps->o.fillSpec.mode,
+ cw->mini.width, cw->mini.height,
+ ps->o.iconFillSpec.alg, ps->o.fillSpec.valg,
+ &ps->o.iconFillSpec.c);
+ break;
+ case CLIDISP_ICON:
+ cw->icon_pict_filled = simg_postprocess(ps,
+ clone_pictw(ps, cw->icon_pict),
+ ps->o.iconFillSpec.mode,
+ cw->mini.width, cw->mini.height,
+ ps->o.iconFillSpec.alg, ps->o.fillSpec.valg,
+ &ps->o.iconFillSpec.c);
+ break;
+ case CLIDISP_THUMBNAIL:
+ break;
+ }
+
+ return true;
}
void
@@ -176,6 +244,8 @@ clientwin_destroy(ClientWin *cw, bool destroyed) {
free_picture(ps, &cw->destination);
free_pixmap(ps, &cw->pixmap);
free_pixmap(ps, &cw->cpixmap);
+ free_pictw(ps, &cw->icon_pict);
+ free_pictw(ps, &cw->icon_pict_filled);
if (cw->src.window && !destroyed) {
free_damage(ps, &cw->damage);
@@ -198,32 +268,53 @@ clientwin_destroy(ClientWin *cw, bool destroyed) {
}
static void
-clientwin_repaint(ClientWin *cw, XRectangle *rect)
-{
- XRenderColor *tint = (cw->focused ? &cw->mainwin->highlightTint
- : &cw->mainwin->normalTint);
- int s_x = (double)rect->x * cw->factor,
- s_y = (double)rect->y * cw->factor,
- s_w = (double)rect->width * cw->factor,
- s_h = (double)rect->height * cw->factor;
-
- if(cw->mainwin->ps->o.lazyTrans)
- {
- XRenderComposite(cw->mainwin->ps->dpy, PictOpSrc, cw->origin,
- cw->focused ? cw->mainwin->highlightPicture : cw->mainwin->normalPicture,
- cw->destination, s_x, s_y, 0, 0, s_x, s_y, s_w, s_h);
+clientwin_repaint(ClientWin *cw, XRectangle *rect) {
+ session_t *ps = cw->mainwin->ps;
+ Picture source = None;
+ int s_x = rect->x, s_y = rect->y,
+ s_w = rect->width, s_h = rect->height;
+
+ switch (cw->mode) {
+ case CLIDISP_NONE:
+ break;
+ case CLIDISP_FILLED:
+ source = cw->pict_filled->pict;
+ break;
+ case CLIDISP_ICON:
+ source = cw->icon_pict_filled->pict;
+ break;
+ case CLIDISP_THUMBNAIL:
+ source = cw->origin;
+ s_x *= cw->factor;
+ s_y *= cw->factor;
+ s_w *= cw->factor;
+ s_h *= cw->factor;
+ break;
+ }
+
+
+ if (!source) return;
+
+ if (ps->o.lazyTrans) {
+ XRenderComposite(ps->dpy, PictOpSrc, source,
+ cw->focused ? cw->mainwin->highlightPicture : cw->mainwin->normalPicture,
+ cw->destination, s_x, s_y, 0, 0, s_x, s_y, s_w, s_h);
+ }
+ else {
+ XRenderComposite(ps->dpy, PictOpSrc, cw->mainwin->background, None, cw->destination, cw->mini.x + s_x, cw->mini.y + s_y, 0, 0, s_x, s_y, s_w, s_h);
+ XRenderComposite(ps->dpy, PictOpOver, source,
+ cw->focused ? cw->mainwin->highlightPicture : cw->mainwin->normalPicture,
+ cw->destination, s_x, s_y, 0, 0, s_x, s_y, s_w, s_h);
}
- else
+
+ // Tinting
{
- XRenderComposite(cw->mainwin->ps->dpy, PictOpSrc, cw->mainwin->background, None, cw->destination, cw->mini.x + s_x, cw->mini.y + s_y, 0, 0, s_x, s_y, s_w, s_h);
- XRenderComposite(cw->mainwin->ps->dpy, PictOpOver, cw->origin,
- cw->focused ? cw->mainwin->highlightPicture : cw->mainwin->normalPicture,
- cw->destination, s_x, s_y, 0, 0, s_x, s_y, s_w, s_h);
+ XRenderColor *tint = (cw->focused ? &cw->mainwin->highlightTint
+ : &cw->mainwin->normalTint);
+ if (tint->alpha)
+ XRenderFillRectangle(cw->mainwin->ps->dpy, PictOpOver, cw->destination, tint, s_x, s_y, s_w, s_h);
}
-
- if(tint->alpha)
- XRenderFillRectangle(cw->mainwin->ps->dpy, PictOpOver, cw->destination, tint, s_x, s_y, s_w, s_h);
-
+
XClearArea(cw->mainwin->ps->dpy, cw->mini.window, s_x, s_y, s_w, s_h, False);
}
@@ -300,11 +391,16 @@ clientwin_map(ClientWin *cw) {
session_t *ps = cw->mainwin->ps;
free_damage(ps, &cw->damage);
- cw->damage = XDamageCreate(ps->dpy, cw->src.window, XDamageReportDeltaRectangles);
- XRenderSetPictureTransform(ps->dpy, cw->origin, &cw->mainwin->transform);
-
+ if (!cw->mode)
+ return;
+
+ if (cw->origin) {
+ cw->damage = XDamageCreate(ps->dpy, cw->src.window, XDamageReportDeltaRectangles);
+ XRenderSetPictureTransform(ps->dpy, cw->origin, &cw->mainwin->transform);
+ }
+
clientwin_render(cw);
-
+
XMapWindow(ps->dpy, cw->mini.window);
XRaiseWindow(ps->dpy, cw->mini.window);
}
diff --git a/src/clientwin.h b/src/clientwin.h
index 9b132a5..597435b 100644
--- a/src/clientwin.h
+++ b/src/clientwin.h
@@ -33,19 +33,24 @@ struct _MainWin;
struct _clientwin_t {
struct _MainWin *mainwin;
+ client_disp_mode_t mode;
Window wid_client;
SkippyWindow src;
bool redirected;
Pixmap cpixmap;
+ pictw_t *pict_filled;
+ pictw_t *icon_pict;
+ pictw_t *icon_pict_filled;
+
SkippyWindow mini;
-
+
Pixmap pixmap;
Picture origin, destination;
Damage damage;
float factor;
-
+
bool focused;
-
+
bool damaged;
/* XserverRegion repair; */
@@ -59,6 +64,41 @@ struct _clientwin_t {
.mainwin = NULL \
}
+static inline client_disp_mode_t
+clientwin_get_disp_mode(session_t *ps, ClientWin *cw) {
+ XWindowAttributes wattr = { };
+ XGetWindowAttributes(ps->dpy, cw->src.window, &wattr);
+
+ for (client_disp_mode_t *p = ps->o.clientDisplayModes; *p; p++) {
+ switch (*p) {
+ case CLIDISP_THUMBNAIL:
+ if (IsViewable == wattr.map_state) return *p;
+ break;
+ case CLIDISP_ICON:
+ if (cw->icon_pict) return *p;
+ break;
+ case CLIDISP_FILLED:
+ case CLIDISP_NONE:
+ return *p;
+ }
+ }
+
+ return CLIDISP_NONE;
+}
+
+static inline void
+clientwin_free_res2(session_t *ps, ClientWin *cw) {
+ free_pictw(ps, &cw->icon_pict_filled);
+}
+
+static inline void
+clientwin_free_res(session_t *ps, ClientWin *cw) {
+ clientwin_free_res2(ps, cw);
+ free_pixmap(ps, &cw->cpixmap);
+ free_picture(ps, &cw->origin);
+ free_pictw(ps, &cw->icon_pict);
+}
+
int clientwin_validate_func(dlist *, void *);
int clientwin_sort_func(dlist *, dlist *, void *);
ClientWin *clientwin_create(struct _MainWin *, Window);
@@ -68,7 +108,8 @@ void clientwin_map(ClientWin *);
void clientwin_unmap(ClientWin *);
int clientwin_handle(ClientWin *, XEvent *);
int clientwin_cmp_func(dlist *, void*);
-void clientwin_update(ClientWin *cw);
+bool clientwin_update(ClientWin *cw);
+bool clientwin_update2(ClientWin *cw);
int clientwin_check_group_leader_func(dlist *l, void *data);
void clientwin_render(ClientWin *);
void clientwin_schedule_repair(ClientWin *cw, XRectangle *area);
diff --git a/src/config.c b/src/config.c
index 5562389..0bdbe5e 100644
--- a/src/config.c
+++ b/src/config.c
@@ -19,6 +19,7 @@
#include "skippy.h"
#include <string.h>
+#include <regex.h>
typedef struct
{
diff --git a/src/img-xlib.c b/src/img-xlib.c
index ec34b1a..93b8376 100644
--- a/src/img-xlib.c
+++ b/src/img-xlib.c
@@ -1,6 +1,115 @@
#include "skippy.h"
-unsigned char *
-simg_argb_to_argb32(unsigned char *data, int width, int height) {
- return data;
+#define ICON_PROP_MAXLEN 1048576
+
+pictw_t *
+simg_load_icon(session_t *ps, Window wid, int desired_size) {
+ pictw_t *pictw = NULL;
+ bool processed = false;
+ int best_width = 0, best_height = 0;
+ float best_scale = 1.0f, best_area = 0.0f;
+ const unsigned char *best_data = NULL;
+
+ if (!pictw) {
+ // _NET_WM_ICON
+ winprop_t prop = wid_get_prop_adv(ps, wid, _NET_WM_ICON, 0, ICON_PROP_MAXLEN, XA_CARDINAL, 32);
+ if (prop.nitems) {
+ int width = 0, height = 0;
+ const long *end = prop.data32 + prop.nitems;
+ // Format: WIDTH HEIGHT DATA (32-bit)
+ int wanted_bytes = 0;
+ for (const long *p = prop.data32; p < end; p += wanted_bytes) {
+ if (p + 2 >= end) {
+ printfef("(%#010lx): %d trailing byte(s).", wid, (int) (end - p));
+ break;
+ }
+ width = p[0];
+ height = p[1];
+ if (width <= 0 || height <= 0) {
+ printfef("(%#010lx): (offset %d, width %d, height %d) Invalid width/height.",
+ wid, (int) (p - prop.data32), width, height);
+ break;
+ }
+ wanted_bytes = 2 + width * height;
+ if ((end - p) < wanted_bytes) {
+ printfef("(%#010lx): (offset %d, width %d, height %d) Not enough bytes (%d/%d).",
+ wid, (int) (p - prop.data32), width, height, (int) (end - p), wanted_bytes);
+ break;
+ }
+ // Prefer larger ones if possible
+ if (best_width >= desired_size && best_height >= desired_size
+ && (width < desired_size || height < desired_size))
+ continue;
+ float scale = MAX(1.0f,
+ MIN((float) best_height / height, (float) best_width / width));
+ float area = width * height * scale * scale;
+ if (area > best_area) {
+ best_width = width;
+ best_height = height;
+ best_scale = scale;
+ best_area = area;
+ best_data = (const unsigned char *) p;
+ }
+ }
+ }
+ if (best_data) {
+ {
+ unsigned char *converted_data = simg_data32_from_long(
+ (const long *) best_data, best_width * best_height);
+ pictw = simg_data_to_pictw(ps, best_width, best_height, 32, converted_data, 0);
+ if (converted_data != best_data)
+ free(converted_data);
+ }
+ if (!pictw)
+ printfef("(%#010lx): Failed to create picture.", wid);
+ if (pictw)
+ printfdf("(%#010lx): (offset %d, width %d, height %d) Loaded.",
+ wid, (int) (best_data - prop.data8), pictw->width, pictw->height);
+ }
+ free_winprop(&prop);
+ }
+
+ if (pictw) goto simg_load_icon_end;
+
+ // WM_HINTS
+ // Our method probably fills 1-8 bit pixmaps as black instead of using
+ // "suitable background/foreground". I hope it isn't a problem, though.
+ {
+ XWMHints *h = XGetWMHints(ps->dpy, wid);
+ if (h && (IconPixmapHint & h->flags) && h->icon_pixmap)
+ pictw = simg_pixmap_to_pictw(ps, 0, 0, 0, h->icon_pixmap, h->icon_mask);
+ sxfree(h);
+
+ // Remove pixmap to prevent it from being freed
+ if (pictw)
+ pictw->pxmap = NULL;
+ }
+
+ if (pictw) goto simg_load_icon_end;
+
+ // KWM_WIN_ICON
+ // Same issue as above.
+ {
+ winprop_t prop = wid_get_prop_adv(ps, wid, KWM_WIN_ICON, 0, 2, KWM_WIN_ICON, 32);
+ if (prop.nitems) {
+ Pixmap pxmap = prop.data32[0],
+ mask = (prop.nitems >= 2 ? prop.data32[1]: None);
+ if (pxmap)
+ pictw = simg_pixmap_to_pictw(ps, 0, 0, 0, pxmap, mask);
+ }
+ free_winprop(&prop);
+ }
+
+ if (pictw) goto simg_load_icon_end;
+
+simg_load_icon_end:
+ // Post-processing
+ if (pictw && !processed) {
+ pictw = simg_postprocess(ps, pictw, PICTPOSP_SCALEK,
+ desired_size, desired_size, ALIGN_MID, ALIGN_MID, NULL);
+ printfdf("(%#010lx): (width %d, height %d) Processed.",
+ wid, pictw->width, pictw->height);
+ }
+
+ return pictw;
}
diff --git a/src/img-xlib.h b/src/img-xlib.h
new file mode 100644
index 0000000..ed9b338
--- /dev/null
+++ b/src/img-xlib.h
@@ -0,0 +1,2 @@
+pictw_t *
+simg_load_icon(session_t *ps, Window wid, int desired_size);
diff --git a/src/img.c b/src/img.c
index 3a9996d..163043b 100644
--- a/src/img.c
+++ b/src/img.c
@@ -33,6 +33,11 @@ pictw_t *
simg_postprocess(session_t *ps, pictw_t *src, enum pict_posp_mode mode,
int twidth, int theight, enum align alg, enum align valg,
const XRenderColor *pc) {
+ static const XRenderColor XRC_TRANS = {
+ .red = 0, .green = 0, .blue = 0, .alpha = 0
+ };
+ if (!pc) pc = &XRC_TRANS;
+
const int depth = 32;
pictw_t *dest = NULL;
bool transformed = false;
@@ -55,19 +60,25 @@ simg_postprocess(session_t *ps, pictw_t *src, enum pict_posp_mode mode,
// Determine composite paramaters. We have to do this before create
// Picture because the width/height may need to be calculated.
int width = src->width, height = src->height;
- if (!twidth) twidth = width;
- if (!theight) theight = height;
+ if (!twidth) twidth = (double) theight / height * width;
+ else if (!theight) theight = (double) twidth / width * height;
double ratio_x = 1.0, ratio_y = 1.0;
switch (mode) {
case PICTPOSP_ORIG: break;
case PICTPOSP_TILE: break;
case PICTPOSP_SCALE:
case PICTPOSP_SCALEK:
+ case PICTPOSP_SCALEE:
+ case PICTPOSP_SCALEEK:
{
if (twidth) ratio_x = (double) twidth / width;
if (theight) ratio_y = (double) theight / height;
- if (PICTPOSP_SCALEK == mode)
+ if (PICTPOSP_SCALEK == mode || PICTPOSP_SCALEEK == mode)
ratio_x = ratio_y = MIN(ratio_x, ratio_y);
+ if (PICTPOSP_SCALEE == mode || PICTPOSP_SCALEEK == mode) {
+ ratio_x = MAX(1.0f, ratio_x);
+ ratio_y = MAX(1.0f, ratio_y);
+ }
width *= ratio_x;
height *= ratio_y;
}
diff --git a/src/img.h b/src/img.h
index 687dd2e..882525f 100644
--- a/src/img.h
+++ b/src/img.h
@@ -17,6 +17,17 @@ rect_crop(XRectangle *pdst, const XRectangle *psrc, const XRectangle *pbound) {
}
/**
+ * @brief Get length of 1 pixel of data for the specified depth.
+ */
+static inline int
+depth_to_len(int depth) {
+ int l = 8;
+ while (depth > l)
+ l *= 2;
+ return l / 8;
+}
+
+/**
* @brief Get X Render format for a specified depth.
*/
static inline const XRenderPictFormat *
@@ -33,44 +44,113 @@ depth_to_rfmt(session_t *ps, int depth) {
}
static inline void
-free_pictw(session_t *ps, pictw_t **ppictw) {
+free_pictw_keeppixmap(session_t *ps, pictw_t **ppictw) {
if (*ppictw) {
- free_pixmap(ps, &(*ppictw)->pxmap);
free_picture(ps, &(*ppictw)->pict);
free(*ppictw);
}
*ppictw = NULL;
}
+static inline void
+free_pictw(session_t *ps, pictw_t **ppictw) {
+ if (*ppictw) {
+ free_pixmap(ps, &(*ppictw)->pxmap);
+ free_pictw_keeppixmap(ps, ppictw);
+ }
+}
+
+/**
+ * @brief Destroy a <code>pictspec_t</code>.
+ */
+static inline void
+free_pictspec(session_t *ps, pictspec_t *p) {
+ free_pictw(ps, &p->img);
+ free(p->path);
+}
+
/**
* @brief Build a pictw_t of specified size and depth.
*/
static inline pictw_t *
-create_pictw(session_t *ps, int width, int height, int depth) {
- pictw_t *pictw = allocchk(calloc(1, sizeof(pictw_t)));
-
- if (!(pictw->pxmap =
- XCreatePixmap(ps->dpy, ps->root, width, height, depth))) {
- printfef("(%d, %d, %d): Failed to create Pixmap.",
- width, height, depth);
- goto create_pictw_err;
+create_pictw_frompixmap(session_t *ps, int width, int height, int depth,
+ Pixmap pxmap) {
+ pictw_t *pictw = NULL;
+
+ if (!pxmap) {
+ printfef("(%d, %d, %d, %#010lx): Missing pixmap.", width, height, depth, pxmap);
+ return NULL;
}
- if (!(pictw->pict = XRenderCreatePicture(ps->dpy, pictw->pxmap,
- depth_to_rfmt(ps, depth), 0, NULL))) {
- printfef("(%d, %d, %d): Failed to create Picture.",
- width, height, depth);
- goto create_pictw_err;
+
+ // Acquire Pixmap info
+ if (!(width && height && depth)) {
+ Window rroot = None;
+ int rx = 0, ry = 0;
+ unsigned rwidth = 0, rheight = 0, rborder_width = 0, rdepth = 0;
+ if (!XGetGeometry(ps->dpy, pxmap,
+ &rroot, &rx, &ry, &rwidth, &rheight, &rborder_width, &rdepth)) {
+ printfef("(%d, %d, %d, %#010lx): Failed to determine pixmap size.", width, height, depth, pxmap);
+ return NULL;
+ }
+ width = rwidth;
+ height = rheight;
+ depth = rdepth;
+ }
+
+ // Sanity check
+ if (!(width && height && depth)) {
+ printfef("(%d, %d, %d, %#010lx): Failed to get pixmap info.", width, height, depth, pxmap);
+ return NULL;
+ }
+
+ // Find X Render format
+ const XRenderPictFormat *rfmt = depth_to_rfmt(ps, depth);
+ if (!rfmt) {
+ printfef("(%d, %d, %d, %#010lx): Failed to find X Render format for depth %d.",
+ width, height, depth, pxmap, depth);
+ return NULL;
}
+
+ // Create Picture
+ pictw = scalloc(1, pictw_t);
+ pictw->pxmap = pxmap;
pictw->width = width;
pictw->height = height;
pictw->depth = depth;
+ if (!(pictw->pict = XRenderCreatePicture(ps->dpy, pictw->pxmap,
+ rfmt, 0, NULL))) {
+ printfef("(%d, %d, %d, %#010lx): Failed to create Picture.",
+ width, height, depth, pxmap);
+ free_pictw(ps, &pictw);
+ }
+
return pictw;
+}
-create_pictw_err:
- free_pictw(ps, &pictw);
+/**
+ * @brief Build a pictw_t of specified size and depth.
+ */
+static inline pictw_t *
+create_pictw(session_t *ps, int width, int height, int depth) {
+ Pixmap pxmap = XCreatePixmap(ps->dpy, ps->root, width, height, depth);
+ if (!pxmap) {
+ printfef("(%d, %d, %d): Failed to create Pixmap.", width, height, depth);
+ return NULL;
+ }
- return NULL;
+ return create_pictw_frompixmap(ps, width, height, depth, pxmap);
+}
+
+static inline pictw_t *
+clone_pictw(session_t *ps, pictw_t *pictw) {
+ if (!pictw) return NULL;
+ pictw_t *new_pictw = create_pictw(ps, pictw->width, pictw->height, pictw->depth);
+ if (!new_pictw) return NULL;
+ XRenderComposite(ps->dpy, PictOpSrc, pictw->pict, None, new_pictw->pict,
+ 0, 0, 0, 0, 0, 0, new_pictw->width, new_pictw->height);
+
+ return new_pictw;
}
pictw_t *
@@ -84,15 +164,88 @@ simg_load_s(session_t *ps, const pictspec_t *spec) {
spec->alg, spec->valg, &spec->c);
}
+static inline bool
+simg_cachespec(session_t *ps, pictspec_t *spec) {
+ free_pictw(ps, &spec->img);
+ if (spec->path
+ && !(spec->img = simg_load(ps, spec->path, PICTPOSP_ORIG, 0, 0,
+ ALIGN_MID, ALIGN_MID, NULL)))
+ return false;
+ return true;
+}
+
pictw_t *
simg_postprocess(session_t *ps, pictw_t *src, enum pict_posp_mode mode,
int twidth, int theight, enum align alg, enum align valg,
const XRenderColor *pc);
static inline pictw_t *
+simg_pixmap_to_pictw(session_t *ps, int width, int height, int depth,
+ Pixmap pxmap, Pixmap mask) {
+ GC gc = None;
+ pictw_t *porig = create_pictw_frompixmap(ps, width, height, depth, pxmap);
+ pictw_t *pmask = NULL;
+ pictw_t *pictw = NULL;
+
+ if (!porig) {
+ printfef("(%d, %d, %d, %#010lx, %#010lx): Failed to create picture for pixmap.",
+ width, height, depth, pxmap, mask);
+ goto simg_pixmap_to_pict_end;
+ }
+
+ if (mask) {
+ if (!(pmask = create_pictw_frompixmap(ps, width, height, depth, mask))) {
+ printfef("(%d, %d, %d, %#010lx, %#010lx): Failed to create picture for mask.",
+ width, height, depth, pxmap, mask);
+ goto simg_pixmap_to_pict_end;
+ }
+ // Probably we should check for width/height consistency between pixmap
+ // and mask...
+ }
+
+ if (!(pictw = create_pictw(ps, porig->width, porig->height, porig->depth))) {
+ printfef("(%d, %d, %d, %#010lx, %#010lx): Failed to create target picture.",
+ width, height, depth, pxmap, mask);
+ goto simg_pixmap_to_pict_end;
+ }
+
+ // Copy content
+ static const XRenderColor XRC_TRANS = {
+ .red = 0, .green = 0, .blue = 0, .alpha = 0
+ };
+ XRenderFillRectangle(ps->dpy, PictOpSrc, pictw->pict, &XRC_TRANS,
+ 0, 0, pictw->width, pictw->height);
+ XRenderComposite(ps->dpy, PictOpSrc, porig->pict, (pmask ? pmask->pict: None),
+ pictw->pict, 0, 0, 0, 0, 0, 0, pictw->width, pictw->height);
+
+ // Does core Xlib handle transparency correctly?
+ /*
+ gc = XCreateGC(ps->dpy, pictw->pxmap, 0, 0);
+ if (!gc) {
+ printfef("(%#010lx, %#010lx, %d, %d, %d): Failed to create GC.",
+ pxmap, mask, width, height, depth);
+ free_pictw(ps, &pictw);
+ goto simg_data_to_pict_end;
+ }
+ if (XCopyArea(ps->dpy, pxmap, pictw->pxmap, gc, 0, 0, width, height, 0, 0)) {
+ }
+ */
+
+simg_pixmap_to_pict_end:
+ free_pictw_keeppixmap(ps, &porig);
+ free_pictw_keeppixmap(ps, &pmask);
+ if (gc)
+ XFreeGC(ps->dpy, gc);
+
+ return pictw;
+}
+
+static inline pictw_t *
simg_data_to_pictw(session_t *ps, int width, int height, int depth,
const unsigned char *data, int bytes_per_line) {
assert(data);
+ data = mmemcpy(data, height
+ * (bytes_per_line ? bytes_per_line: depth_to_len(depth) * width));
pictw_t *pictw = NULL;
GC gc = None;
XImage *img = XCreateImage(ps->dpy, DefaultVisual(ps->dpy, ps->screen),
@@ -161,6 +314,17 @@ simg_data24_tobgr(unsigned char *data, int len) {
}
}
+static inline unsigned char *
+simg_data32_from_long(const long *src, int len) {
+ if (4 == sizeof(long))
+ return (unsigned char *) src;
+
+ uint32_t *data = smalloc(len, uint32_t);
+ for (int i = 0; i < len; ++i)
+ data[i] = src[i];
+ return (unsigned char *) data;
+}
+
static inline void
simg_data32_premultiply(unsigned char *data, int len) {
for (; len > 0; --len) {
diff --git a/src/layout.c b/src/layout.c
index 9f7c0fe..63a758a 100644
--- a/src/layout.c
+++ b/src/layout.c
@@ -34,6 +34,7 @@ layout_run(MainWin *mw, dlist *windows,
// Get total window width and max window width/height
foreach_dlist (windows) {
ClientWin *cw = (ClientWin *) iter->data;
+ if (!cw->mode) continue;
sum_w += cw->src.width;
max_w = MAX(max_w, cw->src.width);
max_h = MAX(max_h, cw->src.height);
@@ -42,6 +43,7 @@ layout_run(MainWin *mw, dlist *windows,
// Vertical layout
foreach_dlist (windows) {
ClientWin *cw = (ClientWin*) iter->data;
+ if (!cw->mode) continue;
dlist *slot_iter = dlist_first(slots);
for (; slot_iter; slot_iter = slot_iter->next) {
dlist *slot = (dlist *) slot_iter->data;
diff --git a/src/skippy.c b/src/skippy.c
index 58da90e..aa194ea 100644
--- a/src/skippy.c
+++ b/src/skippy.c
@@ -85,10 +85,12 @@ parse_align_full(session_t *ps, const char *str, enum align *dest) {
static int
parse_pict_posp_mode(session_t *ps, const char *str, enum pict_posp_mode *dest) {
static const char * const STRS_PICTPOSP[] = {
- [ PICTPOSP_ORIG ] = "orig",
- [ PICTPOSP_SCALE ] = "scale",
- [ PICTPOSP_SCALEK ] = "scalek",
- [ PICTPOSP_TILE ] = "tile",
+ [ PICTPOSP_ORIG ] = "orig",
+ [ PICTPOSP_SCALE ] = "scale",
+ [ PICTPOSP_SCALEK ] = "scalek",
+ [ PICTPOSP_SCALEE ] = "scalee",
+ [ PICTPOSP_SCALEEK ] = "scaleek",
+ [ PICTPOSP_TILE ] = "tile",
};
for (int i = 0; i < CARR_LEN(STRS_PICTPOSP); ++i)
if (str_startswithword(str, STRS_PICTPOSP[i])) {
@@ -246,52 +248,98 @@ parse_pictspec_end:
return true;
}
+static client_disp_mode_t *
+parse_client_disp_mode(session_t *ps, const char *s) {
+ static const struct {
+ client_disp_mode_t mode;
+ const char *name;
+ } ENTRIES[] = {
+ { CLIDISP_NONE, "none" },
+ { CLIDISP_FILLED, "filled" },
+ { CLIDISP_ICON, "icon" },
+ { CLIDISP_THUMBNAIL, "thumbnail" },
+ };
+ static const int ALLOC_STEP = 3;
+ int capacity = 0;
+ client_disp_mode_t *ret = NULL;
+
+ int i = 0;
+ for (; s; ++i) {
+ char *word = NULL;
+ s = str_get_word(s, &word);
+ if (!word)
+ break;
+ if (capacity <= i + 1) {
+ capacity += ALLOC_STEP;
+ ret = srealloc(ret, capacity, client_disp_mode_t);
+ }
+ {
+ bool found = false;
+ for (int j = 0; j < CARR_LEN(ENTRIES); ++j)
+ if (!strcmp(word, ENTRIES[j].name)) {
+ found = true;
+ ret[i] = ENTRIES[j].mode;
+ }
+ if (!found) {
+ printfef("(\"%s\"): Invalid mode \"%s\" ignored.", s, word);
+ --i;
+ }
+ }
+ free(word);
+ }
+
+ if (!i) {
+ free(ret);
+ }
+ else {
+ ret[i] = CLIDISP_NONE;
+ }
+
+ return ret;
+}
+
static dlist *
-update_clients(MainWin *mw, dlist *clients, Bool *touched)
-{
- dlist *stack, *iter;
-
- stack = dlist_first(wm_get_stack(mw->ps));
- iter = clients = dlist_first(clients);
-
- if(touched)
+update_clients(MainWin *mw, dlist *clients, Bool *touched) {
+ dlist *stack = dlist_first(wm_get_stack(mw->ps));
+ clients = dlist_first(clients);
+
+ if (touched)
*touched = False;
- /* Terminate clients that are no longer managed */
- while (iter) {
- ClientWin *cw = (ClientWin *)iter->data;
- if (!dlist_find_data(stack, (void *) cw->src.window)) {
+ // Terminate clients that are no longer managed
+ for (dlist *iter = clients; iter; ) {
+ ClientWin *cw = (ClientWin *) iter->data;
+ if (dlist_find_data(stack, (void *) cw->src.window)
+ && clientwin_update(cw)) {
+ iter = iter->next;
+ }
+ else {
dlist *tmp = iter->next;
- clientwin_destroy((ClientWin *)iter->data, True);
+ clientwin_destroy((ClientWin *) iter->data, True);
clients = dlist_remove(iter);
iter = tmp;
- if(touched)
+ if (touched)
*touched = True;
- continue;
}
- clientwin_update(cw);
- iter = iter->next;
}
XFlush(mw->ps->dpy);
-
- /* Add new clients */
- for(iter = dlist_first(stack); iter; iter = iter->next)
- {
+
+ // Add new clients
+ foreach_dlist (stack) {
ClientWin *cw = (ClientWin *)
dlist_find(clients, clientwin_cmp_func, iter->data);
- if(! cw && (Window)iter->data != mw->window)
- {
+ if (!cw && ((Window) iter->data) != mw->window) {
cw = clientwin_create(mw, (Window)iter->data);
if (!cw) continue;
clients = dlist_add(clients, cw);
clientwin_update(cw);
- if(touched)
+ if (touched)
*touched = True;
}
}
-
+
dlist_free(stack);
-
+
return clients;
}
@@ -351,7 +399,11 @@ do_layout(MainWin *mw, dlist *clients, Window focus, Window leader) {
clientwin_move((ClientWin *) iter->data, factor, xoff, yoff);
}
}
-
+
+ foreach_dlist(mw->cod) {
+ clientwin_update2((ClientWin *) iter->data);
+ }
+
// Get the currently focused window and select which mini-window to focus
{
dlist *iter = dlist_find(mw->cod, clientwin_cmp_func, (void *) focus);
@@ -1007,8 +1059,12 @@ int main(int argc, char *argv[]) {
config_get_double_wrap(config, "general", "updateFreq", &ps->o.updateFreq, -1000.0, 1000.0);
config_get_bool_wrap(config, "general", "lazyTrans", &ps->o.lazyTrans);
config_get_bool_wrap(config, "general", "useNameWindowPixmap", &ps->o.useNameWindowPixmap);
+ config_get_bool_wrap(config, "general", "forceNameWindowPixmap", &ps->o.forceNameWindowPixmap);
config_get_bool_wrap(config, "general", "includeFrame", &ps->o.includeFrame);
config_get_bool_wrap(config, "general", "allowUpscale", &ps->o.allowUpscale);
+ config_get_int_wrap(config, "general", "preferredIconSize", &ps->o.preferredIconSize, 1, INT_MAX);
+ config_get_bool_wrap(config, "general", "showAllDesktops", &ps->o.showAllDesktops);
+ config_get_bool_wrap(config, "general", "showUnmapped", &ps->o.showUnmapped);
config_get_bool_wrap(config, "general", "movePointerOnStart", &ps->o.movePointerOnStart);
config_get_bool_wrap(config, "general", "movePointerOnSelect", &ps->o.movePointerOnSelect);
config_get_bool_wrap(config, "general", "movePointerOnRaise", &ps->o.movePointerOnRaise);
@@ -1026,7 +1082,19 @@ int main(int argc, char *argv[]) {
config_get_int_wrap(config, "tooltip", "tintOpacity", &ps->o.highlight_tintOpacity, 0, 256);
config_get_int_wrap(config, "tooltip", "opacity", &ps->o.tooltip_opacity, 0, 256);
{
- const char *sspec = config_get(config, "general", "background", "");
+ const char *s = config_get(config, "general", "clientDisplayModes", NULL);
+ if (s && !(ps->o.clientDisplayModes = parse_client_disp_mode(ps, s)))
+ return RET_BADARG;
+ if (!ps->o.clientDisplayModes) {
+ static const client_disp_mode_t DEF_CLIDISPM[] = {
+ CLIDISP_THUMBNAIL, CLIDISP_ICON, CLIDISP_FILLED, CLIDISP_NONE
+ };
+ ps->o.clientDisplayModes = allocchk(malloc(sizeof(DEF_CLIDISPM)));
+ memcpy(ps->o.clientDisplayModes, &DEF_CLIDISPM, sizeof(DEF_CLIDISPM));
+ }
+ }
+ {
+ const char *sspec = config_get(config, "general", "background", NULL);
if (sspec && strlen(sspec)) {
pictspec_t spec = PICTSPECT_INIT;
if (!parse_pictspec(ps, sspec, &spec))
@@ -1048,6 +1116,16 @@ int main(int argc, char *argv[]) {
free_pictspec(ps, &spec);
}
}
+ if (!parse_pictspec(ps, config_get(config, "general", "iconFillSpec", "orig mid mid #FFFFFF"), &ps->o.iconFillSpec)
+ || !parse_pictspec(ps, config_get(config, "general", "fillSpec", "orig mid mid #FFFFFF"), &ps->o.fillSpec))
+ return RET_BADARG;
+ if (!simg_cachespec(ps, &ps->o.fillSpec))
+ return RET_BADARG;
+ if (ps->o.iconFillSpec.path
+ && !(ps->o.iconDefault = simg_load(ps, ps->o.iconFillSpec.path,
+ PICTPOSP_SCALEK, ps->o.preferredIconSize, ps->o.preferredIconSize,
+ ALIGN_MID, ALIGN_MID, NULL)))
+ return RET_BADARG;
setlocale(LC_NUMERIC, lc_numeric_old);
free(lc_numeric_old);
@@ -1177,6 +1255,7 @@ main_end:
{
free(ps->o.config_path);
free(ps->o.pipePath);
+ free(ps->o.clientDisplayModes);
free(ps->o.normal_tint);
free(ps->o.highlight_tint);
free(ps->o.tooltip_border);
@@ -1185,6 +1264,8 @@ main_end:
free(ps->o.tooltip_textShadow);
free(ps->o.tooltip_font);
free_pictw(ps, &ps->o.background);
+ free_pictspec(ps, &ps->o.iconFillSpec);
+ free_pictspec(ps, &ps->o.fillSpec);
}
if (ps->dpy)
diff --git a/src/skippy.h b/src/skippy.h
index cf9ef64..d470da8 100644
--- a/src/skippy.h
+++ b/src/skippy.h
@@ -57,7 +57,6 @@
#include <math.h>
#include <unistd.h>
#include <time.h>
-#include <regex.h>
#include <string.h>
#include <inttypes.h>
#include <ctype.h>
@@ -143,6 +142,8 @@ enum pict_posp_mode {
PICTPOSP_ORIG,
PICTPOSP_SCALE,
PICTPOSP_SCALEK,
+ PICTPOSP_SCALEE,
+ PICTPOSP_SCALEEK,
PICTPOSP_TILE,
};
@@ -153,7 +154,16 @@ typedef enum {
} wmpsn_t;
typedef struct {
+ Pixmap pxmap;
+ Picture pict;
+ int height;
+ int width;
+ int depth;
+} pictw_t;
+
+typedef struct {
char *path;
+ pictw_t *img;
enum pict_posp_mode mode;
int twidth;
int theight;
@@ -167,14 +177,6 @@ typedef struct {
}
typedef struct {
- Pixmap pxmap;
- Picture pict;
- int height;
- int width;
- int depth;
-} pictw_t;
-
-typedef struct {
unsigned int key;
enum {
KEYMOD_CTRL = 1 << 0,
@@ -183,6 +185,13 @@ typedef struct {
} mod;
} keydef_t;
+typedef enum {
+ CLIDISP_NONE,
+ CLIDISP_FILLED,
+ CLIDISP_ICON,
+ CLIDISP_THUMBNAIL,
+} client_disp_mode_t;
+
/// @brief Option structure.
typedef struct {
char *config_path;
@@ -198,6 +207,7 @@ typedef struct {
double updateFreq;
bool lazyTrans;
bool useNameWindowPixmap;
+ bool forceNameWindowPixmap;
bool includeFrame;
char *pipePath;
bool movePointerOnStart;
@@ -205,6 +215,14 @@ typedef struct {
bool movePointerOnRaise;
bool allowUpscale;
bool includeAllScreens;
+ bool showAllDesktops;
+ bool showUnmapped;
+ int preferredIconSize;
+ client_disp_mode_t *clientDisplayModes;
+ pictspec_t iconFillSpec;
+ pictw_t *iconDefault;
+ pictspec_t fillSpec;
+ pictw_t *fillImg;
char *buttonImgs[NUM_BUTN];
pictw_t *background;
@@ -247,6 +265,7 @@ typedef struct {
.updateFreq = 10.0, \
.lazyTrans = false, \
.useNameWindowPixmap = false, \
+ .forceNameWindowPixmap = false, \
.includeFrame = false, \
.pipePath = NULL, \
.movePointerOnStart = true, \
@@ -254,6 +273,12 @@ typedef struct {
.movePointerOnRaise = true, \
.allowUpscale = true, \
.includeAllScreens = false, \
+ .preferredIconSize = 48, \
+ .clientDisplayModes = NULL, \
+ .iconFillSpec = PICTSPECT_INIT, \
+ .fillSpec = PICTSPECT_INIT, \
+ .showAllDesktops = true, \
+ .showUnmapped = true, \
.buttonImgs = { NULL }, \
.background = NULL, \
.xinerama_showAll = false, \
@@ -366,6 +391,15 @@ allocchk_(void *ptr, const char *func_name) {
/// @brief Wrapper of allocchk_().
#define allocchk(ptr) allocchk_(ptr, __func__)
+/// @brief Wrapper of malloc().
+#define smalloc(nmemb, type) ((type *) allocchk(malloc((nmemb) * sizeof(type))))
+
+/// @brief Wrapper of calloc().
+#define scalloc(nmemb, type) ((type *) allocchk(calloc((nmemb), sizeof(type))))
+
+/// @brief Wrapper of ralloc().
+#define srealloc(ptr, nmemb, type) ((type *) allocchk(realloc((ptr), (nmemb) * sizeof(type))))
+
/// @brief Return the case string.
/// Use #s here to prevent macro expansion
#define CASESTRRET(s) case s: return #s
@@ -446,6 +480,16 @@ print_timestamp(session_t *ps) {
}
/**
+ * @brief Allocate the space and copy some data.
+ */
+static inline unsigned char *
+mmemcpy(const unsigned char *data, int len) {
+ unsigned char *d = smalloc(len, unsigned char);
+ memcpy(d, data, len);
+ return d;
+}
+
+/**
* @brief Allocate the space and join two strings.
*/
static inline char *
@@ -522,7 +566,7 @@ str_endwith(const char *haystick, const char *needle) {
}
/**
- * @brief Check if a string starts with some words, ignore case.
+ * @brief Check if a string starts with some words.
*/
static inline bool
str_startswithword(const char *haystick, const char *needle) {
@@ -542,6 +586,26 @@ str_startswithwordi(const char *haystick, const char *needle) {
}
/**
+ * @brief Get first word.
+ *
+ * @param dest place to store pointer to a copy of the first word
+ * @return start of next word
+ */
+static inline const char *
+str_get_word(const char *s, char **dest) {
+ *dest = NULL;
+ int i = 0;
+ while (isspace(s[i])) ++i;
+ int start = i;
+ while (!isspace0(s[i])) ++i;
+ if (i - start)
+ *dest = mstrncpy(s + start, i - start);
+ while (isspace(s[i])) ++i;
+ if (!s[i]) return NULL;
+ return &s[i];
+}
+
+/**
* @brief Destroy a <code>Pixmap</code>.
*/
static inline void
@@ -586,14 +650,6 @@ free_region(session_t *ps, XserverRegion *p) {
}
}
-/**
- * @brief Destroy a <code>pictspec_t</code>.
- */
-static inline void
-free_pictspec(session_t *ps, pictspec_t *p) {
- free(p->path);
-}
-
static inline unsigned short
alphaconv(int alpha) {
return MIN(alpha * 256, 65535);
@@ -649,6 +705,7 @@ ev_key_str(XKeyEvent *ev) {
printfef("(): KeyRelease %u (%s) not binded to anything.", \
(ev)->xkey.keycode, ev_key_str(&(ev)->xkey))
+#include "img.h"
#include "wm.h"
#include "clientwin.h"
#include "mainwin.h"
@@ -656,7 +713,7 @@ ev_key_str(XKeyEvent *ev) {
#include "focus.h"
#include "config.h"
#include "tooltip.h"
-#include "img.h"
+#include "img-xlib.h"
#ifdef CFG_LIBPNG
// FreeType uses setjmp.h and libpng-1.2 feels crazy about this...
#define PNG_SKIP_SETJMP_CHECK 1
diff --git a/src/wm.c b/src/wm.c
index 2fe2fee..d5e2e1b 100644
--- a/src/wm.c
+++ b/src/wm.c
@@ -38,7 +38,11 @@ Atom
_NET_CLOSE_WINDOW,
_NET_WM_STATE,
_NET_WM_STATE_SHADED,
- _NET_ACTIVE_WINDOW;
+ _NET_ACTIVE_WINDOW,
+ _NET_WM_ICON,
+
+ // Other atoms
+ KWM_WIN_ICON;
static Atom
/* Generic atoms */
@@ -142,7 +146,10 @@ wm_get_atoms(session_t *ps) {
T_GETATOM(_NET_ACTIVE_WINDOW);
T_GETATOM(_NET_CLOSE_WINDOW);
T_GETATOM(_NET_WM_STATE_SHADED);
-
+ T_GETATOM(_NET_WM_ICON);
+
+ T_GETATOM(KWM_WIN_ICON);
+
T_GETATOM(_WIN_SUPPORTING_WM_CHECK);
T_GETATOM(_WIN_WORKSPACE);
T_GETATOM(_WIN_WORKSPACE_COUNT);
@@ -547,7 +554,7 @@ wm_validate_window(session_t *ps, Window wid) {
prop = wid_get_prop(ps, wid, _NET_WM_STATE, 8192, XA_ATOM, 32);
for (int i = 0; result && i < prop.nitems; i++) {
long v = prop.data32[i];
- if (_NET_WM_STATE_HIDDEN == v)
+ if (!ps->o.showUnmapped && _NET_WM_STATE_HIDDEN == v)
result = false;
else if (ps->o.ignoreSkipTaskbar
&& _NET_WM_STATE_SKIP_TASKBAR == v)
@@ -801,18 +808,18 @@ wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset,
unsigned long nitems = 0, after = 0;
unsigned char *data = NULL;
- if (Success == XGetWindowProperty(ps->dpy, w, atom, offset, length,
- False, rtype, &type, &format, &nitems, &after, &data)
- && nitems && (AnyPropertyType == type || type == rtype)
- && (!rformat || format == rformat)
- && (8 == format || 16 == format || 32 == format)) {
- return (winprop_t) {
- .data8 = data,
- .nitems = nitems,
- .type = type,
- .format = format,
- };
- }
+ if (Success == XGetWindowProperty(ps->dpy, w, atom, offset, length,
+ False, rtype, &type, &format, &nitems, &after, &data)
+ && nitems && (AnyPropertyType == type || type == rtype)
+ && (!rformat || format == rformat)
+ && (8 == format || 16 == format || 32 == format)) {
+ return (winprop_t) {
+ .data8 = data,
+ .nitems = nitems,
+ .type = type,
+ .format = format,
+ };
+ }
sxfree(data);
diff --git a/src/wm.h b/src/wm.h
index 8470208..eac1aa7 100644
--- a/src/wm.h
+++ b/src/wm.h
@@ -39,7 +39,11 @@ extern Atom
_NET_CLOSE_WINDOW,
_NET_WM_STATE,
_NET_WM_STATE_SHADED,
- _NET_ACTIVE_WINDOW;
+ _NET_ACTIVE_WINDOW,
+ _NET_WM_ICON,
+
+ // Other atoms
+ KWM_WIN_ICON;
/// Structure representing Window property value.
typedef struct {
diff --git a/utils/Makefile b/utils/Makefile
new file mode 100644
index 0000000..afd71df
--- /dev/null
+++ b/utils/Makefile
@@ -0,0 +1,2 @@
+xtest-test: xtest-test.c
+ $(CC) $(CFLAGS) $(LDFLAGS) $(shell pkg-config --cflags --libs x11 xtst) -o $@ $^
diff --git a/utils/xtest-test b/utils/xtest-test
new file mode 100755
index 0000000..a99447b
Binary files /dev/null and b/utils/xtest-test differ
diff --git a/utils/xtest-test.c b/utils/xtest-test.c
new file mode 100644
index 0000000..91abb3e
--- /dev/null
+++ b/utils/xtest-test.c
@@ -0,0 +1,24 @@
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XTest.h>
+
+int
+main(int argc, char **argv) {
+ Display *dpy = XOpenDisplay(NULL);
+
+ {
+ int xtest_ev_base = 0, xtest_err_base;
+ int major_ver = 0, minor_ver = 0;
+ if (!XTestQueryExtension(dpy, &xtest_ev_base, &xtest_err_base,
+ &major_ver, &minor_ver)) {
+ return 1;
+ }
+ }
+ XTestFakeButtonEvent(dpy, 3, True, 0);
+ XTestFakeButtonEvent(dpy, 3, False, 0);
+ XFlush(dpy);
+
+ XCloseDisplay(dpy);
+
+ return 0;
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment