commit 1f23df4c204ba0f7b643f8e6ff0bce169c7edbe0
Author: MrJensK <jens.se@icloud.com>
AuthorDate: Thu Aug 6 17:04:41 2026 +0200
Commit: MrJensK <jens.se@icloud.com>
CommitDate: Thu Aug 6 17:04:41 2026 +0200
update
---
README.md | 2 ++
draw.c | 16 ++++++++-----
draw.o | Bin 13968 -> 14504 bytes
main.c | 21 +++++++++++++++++
main.o | Bin 13440 -> 16296 bytes
monitors.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------
monitors.h | 9 ++++++++
monitors.o | Bin 25632 -> 28632 bytes
xrantui | Bin 43736 -> 49440 bytes
9 files changed, 111 insertions(+), 12 deletions(-)
diff --git a/README.md b/README.md
index 96689cf..9ed26e1 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@ Interactive terminal UI for arranging monitors, built with ncurses and Xrandr.
- Visual scaled-down canvas showing all active monitors
- Move monitors with arrow keys — snaps to edges automatically
+- Cycle through each monitor's supported resolutions
- Set primary monitor
- Apply layout instantly via xrandr
- Save layout for persistence across reboots (XDG autostart, `.xprofile`, `.xinitrc`)
@@ -68,6 +69,7 @@ DISPLAY=:0 xrantui
|---|---|
| `Tab` | Select next monitor |
| `← ↑ → ↓` | Move selected monitor (40 px/step, snaps to edges) |
+| `+` / `-` | Cycle selected monitor's resolution (larger / smaller) |
| `p` | Set selected monitor as primary |
| `u` | Undo — revert all monitors to positions at startup |
| `Enter` | Apply layout with xrandr |
diff --git a/draw.c b/draw.c
index 9c2c07b..42d6b07 100644
--- a/draw.c
+++ b/draw.c
@@ -116,13 +116,19 @@ void draw_monitors(Monitor *mons, int count, int selected, int scale,
if (msg) {
mvprintw(rows - 1, 0, " %s", msg);
} else {
+ Monitor *sel = &mons[selected];
+ char modeinfo[24] = "";
+ if (sel->nmodes > 0 && sel->mode_idx >= 0)
+ snprintf(modeinfo, sizeof(modeinfo), " (%d/%d)",
+ sel->mode_idx + 1, sel->nmodes);
+
mvprintw(rows - 1, 0,
- " Tab:välj pil:flytta p:primär u:ångra"
+ " Tab:välj pil:flytta +/-:upplösning p:primär u:ångra"
" Enter:applicera s:spara q:avsluta"
- " | %s @ %d,%d%s",
- mons[selected].name,
- mons[selected].x, mons[selected].y,
- mons[selected].primary ? " [P]" : "");
+ " | %s @ %d,%d %dx%d%s%s",
+ sel->name, sel->x, sel->y, sel->w, sel->h,
+ modeinfo,
+ sel->primary ? " [P]" : "");
}
attroff(COLOR_PAIR(COLOR_STATUS));
}
diff --git a/draw.o b/draw.o
index 9bda0bb..958bbf9 100644
Binary files a/draw.o and b/draw.o differ
diff --git a/main.c b/main.c
index ab3811e..fbf5ad0 100644
--- a/main.c
+++ b/main.c
@@ -92,6 +92,27 @@ int main(void) {
msg = "Primärskärm satt — tryck Enter för att applicera";
break;
+ case '+':
+ case '=':
+ if (mons[selected].nmodes > 0) {
+ monitors_cycle_mode(&mons[selected], -1); /* -1 = större (lista är störst->minst) */
+ snap(mons, nmons, selected);
+ msg = "Upplösning ändrad — tryck Enter för att applicera";
+ } else {
+ msg = "Inga kända upplösningar för denna skärm";
+ }
+ break;
+ case '-':
+ case '_':
+ if (mons[selected].nmodes > 0) {
+ monitors_cycle_mode(&mons[selected], 1); /* +1 = mindre */
+ snap(mons, nmons, selected);
+ msg = "Upplösning ändrad — tryck Enter för att applicera";
+ } else {
+ msg = "Inga kända upplösningar för denna skärm";
+ }
+ break;
+
case 'u':
case 'U':
memcpy(mons, orig, sizeof(Monitor) * (size_t)nmons);
diff --git a/main.o b/main.o
index ec7f18e..7e09235 100644
Binary files a/main.o and b/main.o differ
diff --git a/monitors.c b/monitors.c
index 6012c2b..57777b3 100644
--- a/monitors.c
+++ b/monitors.c
@@ -6,6 +6,52 @@
#include <stdlib.h>
#include <sys/stat.h>
+/* fyller m->modes_w/h med unika WxH från outputens tillgängliga moder,
+ störst yta först, och sätter m->mode_idx till den som matchar m->w/h */
+static void collect_modes(Monitor *m, XRRScreenResources *res, XRROutputInfo *out) {
+ m->nmodes = 0;
+ m->mode_idx = -1;
+
+ for (int i = 0; i < out->nmode && m->nmodes < MAX_MODES; i++) {
+ RRMode id = out->modes[i];
+ XRRModeInfo *mi = NULL;
+ for (int j = 0; j < res->nmode; j++) {
+ if (res->modes[j].id == id) { mi = &res->modes[j]; break; }
+ }
+ if (!mi) continue;
+
+ int w = (int)mi->width, h = (int)mi->height;
+ int dup = 0;
+ for (int k = 0; k < m->nmodes; k++)
+ if (m->modes_w[k] == w && m->modes_h[k] == h) { dup = 1; break; }
+ if (dup) continue;
+
+ /* infoga sorterat efter yta, störst först */
+ int pos = m->nmodes;
+ while (pos > 0 && m->modes_w[pos - 1] * m->modes_h[pos - 1] < w * h) {
+ m->modes_w[pos] = m->modes_w[pos - 1];
+ m->modes_h[pos] = m->modes_h[pos - 1];
+ pos--;
+ }
+ m->modes_w[pos] = w;
+ m->modes_h[pos] = h;
+ m->nmodes++;
+ }
+
+ for (int k = 0; k < m->nmodes; k++)
+ if (m->modes_w[k] == m->w && m->modes_h[k] == m->h) { m->mode_idx = k; break; }
+}
+
+void monitors_cycle_mode(Monitor *m, int dir) {
+ if (m->nmodes == 0) return;
+ int idx = m->mode_idx;
+ if (idx < 0) idx = 0;
+ else idx = (idx + dir + m->nmodes) % m->nmodes;
+ m->mode_idx = idx;
+ m->w = m->modes_w[idx];
+ m->h = m->modes_h[idx];
+}
+
int monitors_get(Monitor *mons, int max) {
Display *dpy = XOpenDisplay(NULL);
if (!dpy) {
@@ -43,6 +89,8 @@ int monitors_get(Monitor *mons, int max) {
m->h = (int)crtc->height;
XRRFreeCrtcInfo(crtc);
}
+
+ collect_modes(m, res, out);
count++;
}
XRRFreeOutputInfo(out);
@@ -65,10 +113,17 @@ static void build_xrandr_args(Monitor *mons, int count, char *buf, size_t bufsz)
int pos = snprintf(buf, bufsz, "xrandr");
for (int i = 0; i < count && pos < (int)bufsz - 80; i++) {
sanitize_name(mons[i].name);
- pos += snprintf(buf + pos, bufsz - (size_t)pos,
- " --output %s --pos %dx%d --auto%s",
- mons[i].name, mons[i].x, mons[i].y,
- mons[i].primary ? " --primary" : "");
+ Monitor *m = &mons[i];
+ if (m->w > 0 && m->h > 0)
+ pos += snprintf(buf + pos, bufsz - (size_t)pos,
+ " --output %s --mode %dx%d --pos %dx%d%s",
+ m->name, m->w, m->h, m->x, m->y,
+ m->primary ? " --primary" : "");
+ else
+ pos += snprintf(buf + pos, bufsz - (size_t)pos,
+ " --output %s --pos %dx%d --auto%s",
+ m->name, m->x, m->y,
+ m->primary ? " --primary" : "");
}
}
@@ -94,9 +149,15 @@ int monitors_save(Monitor *mons, int count) {
fprintf(f, "#!/bin/sh\n");
for (int i = 0; i < count; i++) {
sanitize_name(mons[i].name);
- fprintf(f, "xrandr --output %s --pos %dx%d --auto%s\n",
- mons[i].name, mons[i].x, mons[i].y,
- mons[i].primary ? " --primary" : "");
+ Monitor *m = &mons[i];
+ if (m->w > 0 && m->h > 0)
+ fprintf(f, "xrandr --output %s --mode %dx%d --pos %dx%d%s\n",
+ m->name, m->w, m->h, m->x, m->y,
+ m->primary ? " --primary" : "");
+ else
+ fprintf(f, "xrandr --output %s --pos %dx%d --auto%s\n",
+ m->name, m->x, m->y,
+ m->primary ? " --primary" : "");
}
fclose(f);
chmod(path, 0755);
diff --git a/monitors.h b/monitors.h
index 8fd60e6..5ee3ae0 100644
--- a/monitors.h
+++ b/monitors.h
@@ -2,6 +2,7 @@
#define MONITORS_H
#define MAX_MONITORS 16
+#define MAX_MODES 32
typedef struct {
char name[64];
@@ -9,9 +10,17 @@ typedef struct {
int w, h;
int connected;
int primary;
+
+ /* tillgängliga upplösningar, sorterade störst->minst; mode_idx pekar
+ på den som motsvarar aktuellt w/h (-1 om okänd) */
+ int modes_w[MAX_MODES];
+ int modes_h[MAX_MODES];
+ int nmodes;
+ int mode_idx;
} Monitor;
int monitors_get(Monitor *mons, int max);
+void monitors_cycle_mode(Monitor *m, int dir); /* dir: +1/-1, sätter w/h från modes-listan */
void monitors_apply(Monitor *mons, int count);
int monitors_save(Monitor *mons, int count); /* returnerar 0 vid lyckad sparning */
diff --git a/monitors.o b/monitors.o
index 61ef6ed..2b1c6b3 100644
Binary files a/monitors.o and b/monitors.o differ
diff --git a/xrantui b/xrantui
index 073b8ac..8aa8819 100755
Binary files a/xrantui and b/xrantui differ