Last active
December 22, 2015 02:39
-
-
Save richardgv/6404725 to your computer and use it in GitHub Desktop.
richardgv/skippy-xd: Work in progress (icon rendering)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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