]> git.draconx.ca Git - fvwmconf.git/blob - scripts/C/xaspect.c
Improve wallpaper menu generation.
[fvwmconf.git] / scripts / C / xaspect.c
1 /*
2  * Copyright © 2008, 2017 Nick Bowler
3  *
4  * License WTFPL2: Do What The Fuck You Want To Public License, version 2.
5  * This is free software: you are free to do what the fuck you want to.
6  * There is NO WARRANTY, to the extent permitted by law.
7  */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <math.h>
13 #include <inttypes.h>
14 #include <xcb/xcb.h>
15
16 /* Table of common monitor aspect ratios.  Add to this as necessary. */
17 static const struct aspect {
18         unsigned num, denom;
19 } aspects[] = {
20         { 16, 10 },
21         { 16, 9 },
22         { 8, 3 },
23         { 5, 4 },
24         { 4, 3 }
25 };
26
27 /*
28  * Get the xcb screen structure for the specified screen, or NULL if it doesn't
29  * exist.
30  */
31 static xcb_screen_t *getscreen(xcb_connection_t *c, int screen)
32 {
33         xcb_screen_iterator_t iter;
34
35         iter = xcb_setup_roots_iterator(xcb_get_setup(c));
36         while (iter.rem) {
37                 if (screen == 0)
38                         return iter.data;
39                 screen--;
40                 xcb_screen_next(&iter);
41         }
42
43         return NULL;
44 }
45
46 void find_nearest_aspect(xcb_screen_t *screen, struct aspect *out)
47 {
48         unsigned int w = screen->width_in_millimeters;
49         unsigned int h = screen->height_in_millimeters;
50
51         double r = (double)w/h;
52         double d = HUGE_VAL;
53         int i;
54
55         for (i = 0; i < (sizeof aspects / sizeof aspects[0]); i++) {
56                 double t = (double) aspects[i].num / aspects[i].denom;
57                 double cmp;
58
59                 cmp = fabs(r - t);
60                 if (cmp < d) {
61                         *out = aspects[i];
62                         d = cmp;
63                 }
64
65                 cmp = fabs(r - 1/t);
66                 if (cmp < d) {
67                         out->num = aspects[i].denom;
68                         out->denom = aspects[i].num;
69                         d = cmp;
70                 }
71         }
72 }
73
74 struct options {
75         char *displayname;
76         int showdimensions;
77 } *parseoptions(int argc, char **argv)
78 {
79         static struct options opts = { 0 };
80         int i;
81
82         for (i = 1; i < argc; i++) {
83                 if (strcmp(argv[i], "-display") == 0) {
84                         if (++i < argc) {
85                                 opts.displayname = argv[i];
86                                 continue;
87                         }
88                         fprintf(stderr, "-display requires an argument\n");
89                 } else if (strcmp(argv[i], "-dimensions") == 0) {
90                         opts.showdimensions = 1;
91                 }
92         }
93
94         return &opts;
95 }
96
97 int main(int argc, char **argv)
98 {
99         struct aspect screenaspect;
100         xcb_connection_t *display;
101         xcb_screen_t     *screen;
102         int screen_num;
103
104         struct options *opts = parseoptions(argc, argv);
105         
106         display = xcb_connect(opts->displayname, &screen_num);
107         if (xcb_connection_has_error(display)) {
108                 fprintf(stderr, "Failed to open display.\n");
109                 xcb_disconnect(display);
110                 return EXIT_FAILURE;
111         }
112
113         screen = getscreen(display, screen_num);
114         if (!screen) {
115                 fprintf(stderr, "Invalid screen number.\n");
116                 xcb_disconnect(display);
117                 return EXIT_FAILURE;
118         }
119
120         if (opts->showdimensions) {
121                 printf("%" PRIu16 "x%" PRIu16 "-",
122                        screen->width_in_pixels, screen->height_in_pixels);
123         }
124
125         find_nearest_aspect(screen, &screenaspect);
126         printf("%u:%u\n", screenaspect.num, screenaspect.denom);
127
128         xcb_disconnect(display);
129         return EXIT_SUCCESS;
130 }