foxygit / screen Log in
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