+/* Save mouse start location when moving/resizing windows */
+static void set_move(win_t *win, ptr_t ptr, drag_t drag)
+{
+ printf("set_move: %d - %p@%d,%d\n",
+ drag, win, ptr.rx, ptr.ry);
+ move_mode = drag;
+ if (drag == MOVE || drag == RESIZE) {
+ move_layer = searchl(wm_tag, win, NULL,
+ &move_lcol, &move_lrow, &move_lflt);
+ if (move_layer < 0)
+ return;
+ move_prev = ptr;
+ int midy = win->y + (win->h/2);
+ int midx = win->x + (win->w/2);
+ move_dir.v = ptr.ry < midy ? -1 : +1;
+ move_dir.h = ptr.rx < midx ? -1 : +1;
+ }
+}
+
+/* Print a text representation of the window layout
+ * Quite useful for debugging */
+static void print_txt(void)
+{
+ for (list_t *ltag = wm->tags; ltag; ltag = ltag->next) {
+ tag_t *tag = ltag->data;
+ printf("tag: <%-9p [%p->%p] >%-9p d=%-9p - %s\n",
+ ltag->prev, ltag, ltag->data, ltag->next,
+ tag->dpy, tag->name);
+ for (list_t *ldpy = tag->dpys; ldpy; ldpy = ldpy->next) {
+ dpy_t *dpy = ldpy->data;
+ win_t *geom = dpy->geom;
+ printf(" dpy: <%-9p [%p->%p] >%-9p %c=%-9p - %d,%d %dx%d\n",
+ ldpy->prev, ldpy, ldpy->data, ldpy->next,
+ dpy->layer == TILING ? 'c' : 'f',
+ dpy->layer == TILING ? (void*)dpy->col : (void*)dpy->flt,
+ geom->x, geom->y, geom->h, geom->w);
+ for (list_t *lcol = dpy->cols; lcol; lcol = lcol->next) {
+ col_t *col = lcol->data;
+ printf(" col: <%-9p [%p->%p] >%-9p r=%-9p - %dpx @ %d\n",
+ lcol->prev, lcol, lcol->data, lcol->next,
+ col->row, col->width, col->layout);
+ for (list_t *lrow = col->rows; lrow; lrow = lrow->next) {
+ row_t *row = lrow->data;
+ win_t *win = row->win;
+ printf(" win: <%-9p [%p>>%p] >%-9p focus=%d%d - %4dpx \n",
+ lrow->prev, lrow, win, lrow->next,
+ col->row == row, wm_focus == win, win->h);
+ } }
+ for (list_t *lflt = dpy->flts; lflt; lflt = lflt->next) {
+ flt_t *flt = lflt->data;
+ win_t *win = flt->win;
+ printf(" flt: <%-9p [%p>>%p] >%-9p focus=%d%d - %d,%d %dx%d \n",
+ lflt->prev, lflt, win, lflt->next,
+ dpy->flt == flt, wm_focus == flt->win,
+ flt->x, flt->y, flt->h, flt->w);
+ } } }
+}
+
+/* Cleanly remove a window from a tag
+ * Determines the new focused row/col
+ * Prunes empty lists */
+static layer_t cut_win(win_t *win, tag_t *tag)
+{
+ list_t *ldpy, *lcol, *lrow, *lflt;
+ layer_t layer = searchl(tag, win, &ldpy, &lcol, &lrow, &lflt);
+
+ if (layer == TILING) {
+ dpy_t *dpy = DPY(ldpy);
+ col_t *col = COL(lcol);
+ col->row = lrow->prev ? lrow->prev->data :
+ lrow->next ? lrow->next->data : NULL;
+ col->rows = list_remove(col->rows, lrow, 1);
+ if (col->rows == NULL && (lcol->next || lcol->prev)) {
+ dpy->col = lcol->prev ? lcol->prev->data :
+ lcol->next ? lcol->next->data : NULL;
+ dpy->cols = list_remove(dpy->cols, lcol, 1);
+ }
+ }
+
+ if (layer == FLOATING) {
+ dpy_t *dpy = DPY(ldpy);
+ dpy->flts = list_remove(dpy->flts, lflt, 1);
+ dpy->flt = dpy->flts ? list_last(dpy->flts)->data : NULL;
+ if (!dpy->flt && dpy->col && dpy->col->row)
+ dpy->layer = TILING;
+ }
+
+ return layer;
+}
+
+/* Insert a window into the tiling layer
+ * The window is added immediately after the
+ * columns currently focused row */
+static void put_win_col(win_t *win, tag_t *tag, dpy_t *dpy, col_t *col)
+{
+ row_t *row = new0(row_t);
+ row->win = win;
+ row->state = win->state ?: ST_SHOW;
+
+ if (col == NULL) {
+ col = new0(col_t);
+ dpy->cols = list_insert(dpy->cols, col);
+ }
+
+ int nrows = list_length(col->rows);
+ if (col->row) {
+ list_t *prev = list_find(col->rows, col->row);
+ list_insert_after(prev, row);
+ } else {
+ col->rows = list_insert(col->rows, row);
+ }
+ tag->dpy = dpy;
+ tag->dpy->col = col;
+ tag->dpy->col->row = row;
+ tag->dpy->layer = TILING;
+
+ row->height = dpy->geom->h / MAX(nrows,1);
+ if (nrows == 0) {
+ int ncols = list_length(dpy->cols);
+ col->width = dpy->geom->w / MAX(ncols-1,1);
+ }
+}
+
+/* Insert a window into the floating layer */
+static void put_win_flt(win_t *win, tag_t *tag, dpy_t *dpy)
+{
+ flt_t *flt = new0(flt_t);
+ flt->win = win;
+ flt->w = dpy->geom->w / 2;
+ flt->h = dpy->geom->h / 2;
+ flt->x = dpy->geom->x + flt->w / 2;
+ flt->y = dpy->geom->y + flt->h / 2;
+ flt->state = win->state ?: ST_SHOW;
+ if (dpy->flt) {
+ flt->x = dpy->flt->x + 20;
+ flt->y = dpy->flt->y + 20;
+ }
+ dpy->flts = list_append(dpy->flts, flt);
+ tag->dpy = dpy;
+ tag->dpy->flt = flt;
+ tag->dpy->layer = FLOATING;
+}
+
+/* Insert a window into a tag */
+static void put_win(win_t *win, tag_t *tag, layer_t layer)
+{
+ if (layer == TILING)
+ put_win_col(win, tag, tag->dpy, tag->dpy->col);
+ if (layer == FLOATING)
+ put_win_flt(win, tag, tag->dpy);
+}
+
+/* Move a window up, down, left, or right
+ * This handles moving with a column, between
+ * columns, and between multiple monitors. */
+static void shift_window(win_t *win, int col, int row)
+{
+ if (!win) return;
+ printf("shift_window: %p - %+d,%+d\n", win, col, row);
+ print_txt();
+ printf("shift_window: >>>\n");
+ list_t *ldpy, *lcol, *lrow, *lflt;
+ if (TILING != searchl(wm_tag, win, &ldpy, &lcol, &lrow, &lflt))
+ return;
+ dpy_t *dpy = ldpy->data;
+ if (row != 0) {
+ /* Move with a column, just swap rows */
+ list_t *src = lrow, *dst = NULL;
+ if (row < 0) dst = src->prev;
+ if (row > 0) dst = src->next;
+ if (src && dst) {
+ printf("swap: %p <-> %p\n", src->data, dst->data);
+ row_t *tmp = src->data;
+ src->data = dst->data;
+ dst->data = tmp;
+ goto update;
+ }
+ } else {
+ /* Moving between columns */
+ int onlyrow = !lrow->prev && !lrow->next;
+ list_t *src = lcol, *dst = NULL;
+ if (col < 0) {
+ if (src->prev) {
+ /* Normal move between columns */
+ dst = src->prev;
+ } else if (!onlyrow) {
+ /* Create new column */
+ dpy->cols = list_insert(dpy->cols, new0(col_t));
+ dst = src->prev;
+ } else if (ldpy->prev) {
+ /* Move to next monitor */
+ dpy = ldpy->prev->data;
+ dst = list_last(dpy->cols);
+ } else {
+ /* We, shall, not, be,
+ * we shall not be moved */
+ return;
+ }
+ }
+ if (col > 0) {
+ if (src->next) {
+ dst = src->next;
+ } else if (!onlyrow) {
+ dpy->cols = list_append(dpy->cols, new0(col_t));
+ dst = src->next;
+ } else if (ldpy->next) {
+ dpy = ldpy->next->data;
+ dst = dpy->cols;
+ } else {
+ return;
+ }
+ }
+ cut_win(win, wm_tag);
+ put_win_col(win, wm_tag, dpy, dst ? dst->data : NULL);
+ goto update;
+ }
+update:
+ print_txt();
+ wm_update();
+}
+
+/* Get next/prev item, with wraparound */
+static list_t *get_next(list_t *list, int forward)
+{
+ list_t *next = forward ? list->next : list->prev;
+ if (next == NULL) {
+ next = list;
+ while ((list = forward ? next->prev : next->next))
+ next = list;
+ }
+ return next;
+}
+
+/* Move keyboard focus in a given direction */
+static void shift_focus(int cols, int rows)
+{
+ printf("shift_focus: %+d,%+d\n", cols, rows);
+ if (rows != 0 && wm_focus) {
+ /* Move focus up/down */
+ list_t *dpy, *col, *row;
+ if (TILING != searchl(wm_tag, wm_focus, &dpy, &col, &row, NULL))
+ return;
+ row_t *next = get_next(row, rows > 0)->data;
+ set_focus(next->win);
+ if (COL(col)->layout != SPLIT)
+ wm_update();
+ }
+ if (cols != 0) {
+ /* Move focus left/right */
+ list_t *dpy, *col, *row, *ndpy, *ncol = NULL;
+ if (wm_focus) {
+ /* Currently focused on a window */
+ if (TILING != searchl(wm_tag, wm_focus, &dpy, &col, &row, NULL))
+ return;
+ ncol = cols > 0 ? col->next : col->prev;
+ } else {
+ /* Currently focused on an empty display */
+ dpy = list_find(wm_tag->dpys, wm_dpy);
+ }
+ if (ncol == NULL) {
+ /* Moving focus to a different display */
+ ndpy = get_next(dpy, cols > 0);
+ ncol = cols > 0 ? DPY(ndpy)->cols :
+ list_last(DPY(ndpy)->cols);
+ wm_dpy = ndpy->data;
+ }
+ if (ncol && COL(ncol) && COL(ncol)->row)
+ set_focus(COL(ncol)->row->win);
+ else
+ sys_focus(wm->root);
+ }
+}
+
+/* Raise the window in the floating */
+static void raise_float(win_t *win)
+{
+ printf("raise_float: %p\n", win);
+ list_t *cur;
+ for (cur = wm_dpy->flts; cur; cur = cur->next)
+ if (FLT(cur)->win == win)
+ break;
+ if (cur) {
+ flt_t *flt = cur->data;
+ wm_dpy->flts = list_remove(wm_dpy->flts, cur, 0);
+ wm_dpy->flts = list_append(wm_dpy->flts, flt);