swim.git

mon.c

espurr
/* swim - window manager
 * Copyright (C) 2024 ArcNyxx
 * see LICENCE file for licensing information */

#include <stdint.h>
#include <stdlib.h>

#include <xcb/xcb.h>
#include <xcb/render.h>
#include <xcb/xcb_renderutil.h>
#include <xcb/xinerama.h>

#include "mon.h"
#include "swim.h"
#include "tile.h"
#include "util.h"

static mon_t *mkmon(xcb_connection_t *c, xcb_screen_t *scr,
		xcb_render_pictformat_t fmt, uint16_t x, uint16_t y,
		uint16_t w, uint16_t h);
static void mvmon(xcb_connection_t *c, mon_t *mon, uint16_t x, uint16_t y,
		uint16_t w, uint16_t h);
static void rmmon(xcb_connection_t *c, mon_t **mp, mon_t *mons);

static mon_t *
mkmon(xcb_connection_t *c, xcb_screen_t *scr, xcb_render_pictformat_t fmt,
		uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
	mon_t *mon = srealloc(NULL, sizeof(mon_t));
	*mon = (mon_t){ .x = x, .y = y, .w = w, .h = h, .tags = 1, .mfact = 50,
			.nmaster = 1, .show = true, .gap = true, .top = true,
			.win = ID(c), .pic = ID(c) };
#define CWM1 XCB_CW_OVERRIDE_REDIRECT | XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK
#define ARR1 (uint32_t []){ true, XCB_BACK_PIXMAP_PARENT_RELATIVE, \
		XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS }
	xcb_create_window(c, XCB_COPY_FROM_PARENT, mon->win, scr->root,
			x, y + !mon->top * (h - H), w, H, 0,
			XCB_WINDOW_CLASS_COPY_FROM_PARENT,
			XCB_COPY_FROM_PARENT, CWM1, ARR1);
	xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->win,
			XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8,
			10, "swim\0swim");
	xcb_render_create_picture(c, mon->pic, mon->win, fmt, 0, NULL);
	xcb_map_window(c, mon->win);
	return mon;
}

static void
rmmon(xcb_connection_t *c, mon_t **mp, mon_t *mons)
{
	mon_t *mon = *mp; *mp = mon->next;

	cli_t **cli;
	for (cli = &mon->cli; *cli != NULL; cli = &(*cli)->next)
		(*cli)->mon = mons;
	*cli = mons->cli; mons->cli = mon->cli;
	tile(mons);

	xcb_render_free_picture(c, mon->pic);
	xcb_destroy_window(c, mon->win);
	free(mon);
}

static void
mvmon(xcb_connection_t *c, mon_t *mon, uint16_t x, uint16_t y,
		uint16_t w, uint16_t h)
{
	mon->x = x, mon->y = y, mon->w = w, mon->h = h;
	for (cli_t *cli = mon->cli; cli != NULL; cli = cli->next)
		if (cli->fsc)
#define CWM2 XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | \
		XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT
#define ARR2 (uint32_t []){ x, y, w, h }
			xcb_configure_window(c, cli->win, CWM2, ARR2);
	tile(mon);
#define ARR3 (uint32_t []){ x, mon->show ? y + !mon->top * (h - H) : -H, w, H }
	xcb_configure_window(c, mon->win, CWM2, ARR3);
}

void
chmon(xcb_connection_t *c, xcb_screen_t *scr, xcb_render_pictformat_t fmt,
		mon_t **mp)
{
	xcb_xinerama_is_active_reply_t *act = xcb_xinerama_is_active_reply(c,
			xcb_xinerama_is_active(c), NULL);
	if (!act->state)
		die("swim: xinerama inactive");
	free(act);

	xcb_xinerama_query_screens_reply_t *dat =
			xcb_xinerama_query_screens_reply(c,
			xcb_xinerama_query_screens_unchecked(c), NULL);
	xcb_xinerama_screen_info_iterator_t iter =
			xcb_xinerama_query_screens_screen_info_iterator(dat);
	if (iter.rem == 0)
		die("swim: no monitors available");

	mon_t *mons = *mp;
	for (int new = 0; iter.rem > 0; xcb_xinerama_screen_info_next(&iter)) {
		const uint16_t x = iter.data->x_org, y = iter.data->y_org,
				w = iter.data->width, h = iter.data->height;
		mon_t *mon = mons; int j = 0;
		for (; j < new && mon != NULL; ++j, mon = mon->next)
			if (mon->x != x || mon->y != y || mon->w != w ||
					mon->h != h)
				break;
		if (j == new || mon == NULL) {
			if (*mp != NULL)
				mvmon(c, *mp, x, y, w, h);
			else
				*mp = mkmon(c, scr, fmt, x, y, w, h);
			++new, mp = &(*mp)->next;
		}
	}
	while (*mp != NULL)
		rmmon(c, mp, mons);
	free(dat);
}