A mirror of phillbush's xmenu.
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. #include <err.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <X11/Xlib.h>
  7. #include <X11/Xutil.h>
  8. /* macros */
  9. #define LEN(x) (sizeof (x) / sizeof (x[0]))
  10. #define MAX(x,y) ((x)>(y)?(x):(y))
  11. /* color enum */
  12. enum {ColorFG, ColorBG, ColorLast};
  13. /* draw context structure */
  14. struct DC {
  15. unsigned long unpressed[ColorLast];
  16. unsigned long pressed[ColorLast];
  17. unsigned long decoration[ColorLast];
  18. Drawable d;
  19. GC gc;
  20. XFontStruct *font;
  21. int fonth;
  22. };
  23. /* menu geometry structure */
  24. struct Geometry {
  25. int itemb; /* item border */
  26. int itemw; /* item width */
  27. int itemh; /* item height */
  28. int border; /* window border width */
  29. int separator; /* menu separator width */
  30. };
  31. /* screen geometry structure */
  32. struct ScreenGeometry {
  33. int cursx, cursy; /* cursor position */
  34. int screenw, screenh; /* screen width and height */
  35. };
  36. /* menu item structure */
  37. struct Item {
  38. char *label;
  39. char *output;
  40. int y;
  41. int h;
  42. size_t labellen;
  43. struct Item *next;
  44. struct Menu *submenu;
  45. };
  46. /* menu structure */
  47. struct Menu {
  48. struct Menu *parent;
  49. struct Item *list;
  50. struct Item *selected;
  51. int x, y, w, h;
  52. unsigned level;
  53. Drawable pixmap;
  54. Window win;
  55. };
  56. /* function declarations */
  57. static unsigned long getcolor(const char *s);
  58. static void setupdc(void);
  59. static void setupgeom(void);
  60. static void setupgrab(void);
  61. static struct Item *allocitem(const char *label, const char *output);
  62. static struct Menu *allocmenu(struct Menu *parent, struct Item *list, unsigned level);
  63. static void getmenuitem(Window win, int y, struct Menu **menu_ret, struct Item **item_ret);
  64. static void drawmenu(void);
  65. static void calcscreengeom(void);
  66. static void calcmenu(struct Menu *menu);
  67. static void setcurrmenu(struct Menu *currmenu_new);
  68. static void parsestdin(void);
  69. static void run(void);
  70. static void freewindow(struct Menu *menu);
  71. static void cleanupexit(void);
  72. static void usage(void);
  73. /* X variables */
  74. static Colormap colormap;
  75. static Display *dpy;
  76. static Window rootwin;
  77. static int screen;
  78. static struct DC dc;
  79. /* menu variables */
  80. static struct Menu *rootmenu = NULL;
  81. static struct Menu *currmenu = NULL;
  82. /* geometry variables */
  83. static struct Geometry geom;
  84. static struct ScreenGeometry screengeom;
  85. /* flag variables */
  86. static Bool override_redirect = True;
  87. #include "config.h"
  88. int
  89. main(int argc, char *argv[])
  90. {
  91. int ch;
  92. while ((ch = getopt(argc, argv, "w")) != -1) {
  93. switch (ch) {
  94. case 'w':
  95. override_redirect = False;
  96. break;
  97. default:
  98. usage();
  99. break;
  100. }
  101. }
  102. argc -= optind;
  103. argv += optind;
  104. /* open connection to server and set X variables */
  105. if ((dpy = XOpenDisplay(NULL)) == NULL)
  106. errx(1, "cannot open display");
  107. screen = DefaultScreen(dpy);
  108. rootwin = RootWindow(dpy, screen);
  109. colormap = DefaultColormap(dpy, screen);
  110. /* setup */
  111. setupdc();
  112. setupgeom();
  113. setupgrab();
  114. /* generate menus and recalculate them */
  115. parsestdin();
  116. if (rootmenu == NULL)
  117. errx(1, "no menu generated");
  118. calcscreengeom();
  119. calcmenu(rootmenu);
  120. /* run event loop */
  121. run();
  122. return 1; /* UNREACHABLE */
  123. }
  124. /* get color from color string */
  125. static unsigned long
  126. getcolor(const char *s)
  127. {
  128. XColor color;
  129. if(!XAllocNamedColor(dpy, colormap, s, &color, &color))
  130. errx(1, "cannot allocate color: %s", s);
  131. return color.pixel;
  132. }
  133. /* init draw context */
  134. static void
  135. setupdc(void)
  136. {
  137. /* get color pixels */
  138. dc.unpressed[ColorBG] = getcolor(UNPRESSEDBG);
  139. dc.unpressed[ColorFG] = getcolor(UNPRESSEDFG);
  140. dc.pressed[ColorBG] = getcolor(PRESSEDBG);
  141. dc.pressed[ColorFG] = getcolor(PRESSEDFG);
  142. dc.decoration[ColorBG] = getcolor(DECORATIONBG);
  143. dc.decoration[ColorFG] = getcolor(DECORATIONFG);
  144. /* try to get font */
  145. if ((dc.font = XLoadQueryFont(dpy, FONT)) == NULL)
  146. errx(1, "cannot load font");
  147. dc.fonth = dc.font->ascent + dc.font->descent;
  148. /* create GC and set its font */
  149. dc.gc = XCreateGC(dpy, rootwin, 0, NULL);
  150. XSetFont(dpy, dc.gc, dc.font->fid);
  151. }
  152. /* init menu geometry values */
  153. static void
  154. setupgeom(void)
  155. {
  156. geom.itemb = ITEMB;
  157. geom.itemh = dc.fonth + ITEMB * 2;
  158. geom.itemw = ITEMW;
  159. geom.border = BORDER;
  160. geom.separator = SEPARATOR;
  161. }
  162. /* grab pointer */
  163. static void
  164. setupgrab(void)
  165. {
  166. XGrabPointer(dpy, rootwin, True, ButtonPressMask | ButtonReleaseMask,
  167. GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
  168. }
  169. /* allocate an item */
  170. static struct Item *
  171. allocitem(const char *label, const char *output)
  172. {
  173. struct Item *item;
  174. if ((item = malloc(sizeof *item)) == NULL)
  175. err(1, "malloc");
  176. if (*label == '\0') {
  177. item->label = NULL;
  178. item->output = NULL;
  179. } else {
  180. if ((item->label = strdup(label)) == NULL)
  181. err(1, "strdup");
  182. if ((item->output = strdup(output)) == NULL)
  183. err(1, "strdup");
  184. }
  185. item->y = 0;
  186. item->h = item->label ? geom.itemh : geom.separator;
  187. if (item->label == NULL)
  188. item->labellen = 0;
  189. else
  190. item->labellen = strlen(item->label);
  191. item->next = NULL;
  192. item->submenu = NULL;
  193. return item;
  194. }
  195. /* allocate a menu */
  196. static struct Menu *
  197. allocmenu(struct Menu *parent, struct Item *list, unsigned level)
  198. {
  199. XSetWindowAttributes swa;
  200. struct Menu *menu;
  201. if ((menu = malloc(sizeof *menu)) == NULL)
  202. err(1, "malloc");
  203. menu->parent = parent;
  204. menu->list = list;
  205. menu->selected = NULL;
  206. menu->w = geom.itemw;
  207. menu->h = 0; /* calculated by calcmenu() */
  208. menu->x = 0; /* calculated by calcmenu() */
  209. menu->y = 0; /* calculated by calcmenu() */
  210. menu->level = level;
  211. swa.override_redirect = override_redirect;
  212. swa.background_pixel = dc.decoration[ColorBG];
  213. swa.border_pixel = dc.decoration[ColorFG];
  214. swa.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask
  215. | PointerMotionMask | LeaveWindowMask;
  216. menu->win = XCreateWindow(dpy, rootwin, 0, 0, geom.itemw, geom.itemh, geom.border,
  217. CopyFromParent, CopyFromParent, CopyFromParent,
  218. CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWEventMask,
  219. &swa);
  220. return menu;
  221. }
  222. /* create menus and items from the stdin */
  223. static void
  224. parsestdin(void)
  225. {
  226. char *s, buf[BUFSIZ];
  227. char *label, *output;
  228. unsigned level = 0;
  229. unsigned i;
  230. struct Item *curritem = NULL; /* item currently being read */
  231. struct Menu *prevmenu = NULL; /* menu the previous item was added to */
  232. struct Item *item; /* dummy item for for loops */
  233. struct Menu *menu; /* dummy menu for for loops */
  234. size_t count = 0; /* number of items in the current menu */
  235. while (fgets(buf, BUFSIZ, stdin) != NULL) {
  236. level = 0;
  237. s = buf;
  238. while (*s == '\t') {
  239. level++;
  240. s++;
  241. }
  242. label = output = s;
  243. while (*s != '\0' && *s != '\t' && *s != '\n')
  244. s++;
  245. while (*s == '\t')
  246. *s++ = '\0';
  247. if (*s != '\0' && *s != '\n')
  248. output = s;
  249. while (*s != '\0' && *s != '\n')
  250. s++;
  251. if (*s == '\n')
  252. *s = '\0';
  253. curritem = allocitem(label, output);
  254. if (prevmenu == NULL) { /* there is no menu yet */
  255. menu = allocmenu(NULL, curritem, level);
  256. rootmenu = menu;
  257. prevmenu = menu;
  258. count = 1;
  259. } else if (level < prevmenu->level) { /* item is continuation of a parent menu*/
  260. for (menu = prevmenu, i = level;
  261. menu != NULL && i < prevmenu->level;
  262. menu = menu->parent, i++)
  263. ;
  264. if (menu == NULL)
  265. errx(1, "reached NULL menu");
  266. for (item = menu->list; item->next != NULL; item = item->next)
  267. ;
  268. item->next = curritem;
  269. prevmenu = menu;
  270. } else if (level == prevmenu->level) { /* item is a continuation of current menu */
  271. for (item = prevmenu->list; item->next != NULL; item = item->next)
  272. ;
  273. item->next = curritem;
  274. } else if (level > prevmenu->level) { /* item begins a new menu */
  275. menu = allocmenu(prevmenu, curritem, level);
  276. for (item = prevmenu->list; item->next != NULL; item = item->next)
  277. ;
  278. item->submenu = menu;
  279. prevmenu = menu;
  280. }
  281. count++;
  282. }
  283. }
  284. /* calculate screen geometry */
  285. static void
  286. calcscreengeom(void)
  287. {
  288. Window w1, w2; /* unused variables */
  289. int a, b; /* unused variables */
  290. unsigned mask; /* unused variable */
  291. XQueryPointer(dpy, rootwin, &w1, &w2, &screengeom.cursx, &screengeom.cursy, &a, &b, &mask);
  292. screengeom.screenw = DisplayWidth(dpy, screen);
  293. screengeom.screenh = DisplayHeight(dpy, screen);
  294. }
  295. /* recursivelly calculate height and position of the menus */
  296. static void
  297. calcmenu(struct Menu *menu)
  298. {
  299. XWindowChanges changes;
  300. XSizeHints sizeh;
  301. struct Item *item;
  302. int labelwidth;
  303. /* calculate items positions and menu width and height */
  304. menu->w = geom.itemw;
  305. for (item = menu->list; item != NULL; item = item->next) {
  306. item->y = menu->h;
  307. if (item->label == NULL) /* height for separator item */
  308. menu->h += geom.separator;
  309. else
  310. menu->h += geom.itemh;
  311. labelwidth = XTextWidth(dc.font, item->label, item->labellen) + dc.fonth * 2;
  312. menu->w = MAX(menu->w, labelwidth);
  313. }
  314. /* calculate menu's x and y positions */
  315. if (menu->parent == NULL) { /* if root menu, calculate in respect to cursor */
  316. if (screengeom.screenw - screengeom.cursx >= menu->w)
  317. menu->x = screengeom.cursx;
  318. else if (screengeom.cursx > menu->w)
  319. menu->x = screengeom.cursx - menu->w;
  320. if (screengeom.screenh - screengeom.cursy >= menu->h)
  321. menu->y = screengeom.cursy;
  322. else if (screengeom.screenh > menu->h)
  323. menu->y = screengeom.screenh - menu->h;
  324. } else { /* else, calculate in respect to parent menu */
  325. /* search for the item in parent menu that generates this menu */
  326. for (item = menu->parent->list; item->submenu != menu; item = item->next)
  327. ;
  328. if (screengeom.screenw - (menu->parent->x + menu->parent->w) >= menu->w)
  329. menu->x = menu->parent->x + menu->parent->w;
  330. else if (menu->parent->x > menu->w)
  331. menu->x = menu->parent->x - menu->w;
  332. if (screengeom.screenh - (item->y + menu->parent->y) > menu->h)
  333. menu->y = item->y + menu->parent->y;
  334. else if (screengeom.screenh - menu->parent->y > menu->h)
  335. menu->y = menu->parent->y;
  336. else if (screengeom.screenh > menu->h)
  337. menu->y = screengeom.screenh - menu->h;
  338. }
  339. /* update menu geometry */
  340. changes.height = menu->h;
  341. changes.width = menu->w;
  342. changes.x = menu->x;
  343. changes.y = menu->y;
  344. XConfigureWindow(dpy, menu->win, CWWidth | CWHeight | CWX | CWY, &changes);
  345. /* set window manager size hints */
  346. sizeh.flags = PMaxSize | PMinSize;
  347. sizeh.min_width = sizeh.max_width = menu->w;
  348. sizeh.min_height = sizeh.max_height = menu->h;
  349. XSetWMNormalHints(dpy, menu->win, &sizeh);
  350. /* create pixmap */
  351. menu->pixmap = XCreatePixmap(dpy, menu->win, menu->w, menu->h,
  352. DefaultDepth(dpy, screen));
  353. /* calculate positions of submenus */
  354. for (item = menu->list; item != NULL; item = item->next) {
  355. if (item->submenu != NULL)
  356. calcmenu(item->submenu);
  357. }
  358. }
  359. /* get menu and item of given window and position */
  360. static void
  361. getmenuitem(Window win, int y,
  362. struct Menu **menu_ret, struct Item **item_ret)
  363. {
  364. struct Menu *menu = NULL;
  365. struct Item *item = NULL;
  366. for (menu = currmenu; menu != NULL; menu = menu->parent) {
  367. if (menu->win == win) {
  368. for (item = menu->list; item != NULL; item = item->next) {
  369. if (y >= item->y && y <= item->y + item->h) {
  370. goto done;
  371. }
  372. }
  373. }
  374. }
  375. done:
  376. *menu_ret = menu;
  377. *item_ret = item;
  378. }
  379. /* set currentmenu to menu, umap previous menus and map current menu and its parents */
  380. static void
  381. setcurrmenu(struct Menu *currmenu_new)
  382. {
  383. struct Menu *menu;
  384. if (currmenu_new == currmenu)
  385. return;
  386. for (menu = currmenu; menu != NULL; menu = menu->parent) {
  387. XUnmapWindow(dpy, menu->win);
  388. }
  389. currmenu = currmenu_new;
  390. for (menu = currmenu; menu != NULL; menu = menu->parent)
  391. XMapWindow(dpy, menu->win);
  392. }
  393. /* draw items of the current menu and of its ancestors */
  394. static void
  395. drawmenu(void)
  396. {
  397. struct Menu *menu;
  398. struct Item *item;
  399. for (menu = currmenu; menu != NULL; menu = menu->parent) {
  400. for (item = menu->list; item != NULL; item = item->next) {
  401. unsigned long *color;
  402. int labelx, labely;
  403. /* determine item color */
  404. if (item->label == NULL)
  405. color = dc.decoration;
  406. else if (item == menu->selected)
  407. color = dc.pressed;
  408. else
  409. color = dc.unpressed;
  410. /* draw item box */
  411. XSetForeground(dpy, dc.gc, color[ColorBG]);
  412. XFillRectangle(dpy, menu->pixmap, dc.gc, 0, item->y,
  413. menu->w, item->h);
  414. /* continue if item is a separator */
  415. if (item->label == NULL)
  416. continue;
  417. /* draw item label */
  418. labelx = 0 + dc.fonth;
  419. labely = item->y + dc.fonth + geom.itemb;
  420. XSetForeground(dpy, dc.gc, color[ColorFG]);
  421. XDrawString(dpy, menu->pixmap, dc.gc, labelx, labely,
  422. item->label, item->labellen);
  423. /* draw triangle, if item contains a submenu */
  424. if (item->submenu != NULL) {
  425. int trianglex = menu->w - dc.fonth + geom.itemb - 1;
  426. int triangley = item->y + (3 * item->h)/8 -1;
  427. XPoint triangle[] = {
  428. {trianglex, triangley},
  429. {trianglex + item->h/8 + 1, item->y + item->h/2},
  430. {trianglex, triangley + item->h/4 + 2},
  431. {trianglex, triangley}
  432. };
  433. XFillPolygon(dpy, menu->pixmap, dc.gc, triangle, LEN(triangle),
  434. Convex, CoordModeOrigin);
  435. }
  436. XCopyArea(dpy, menu->pixmap, menu->win, dc.gc, 0, item->y,
  437. menu->w, item->h, 0, item->y);
  438. }
  439. }
  440. }
  441. /* run event loop */
  442. static void
  443. run(void)
  444. {
  445. struct Menu *menu;
  446. struct Item *item;
  447. struct Item *previtem = NULL;
  448. XEvent ev;
  449. setcurrmenu(rootmenu);
  450. while (!XNextEvent(dpy, &ev)) {
  451. switch(ev.type) {
  452. case Expose:
  453. drawmenu();
  454. break;
  455. case MotionNotify:
  456. getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
  457. if (menu != NULL && item != NULL) {
  458. if (previtem != item) {
  459. if (item->submenu != NULL)
  460. setcurrmenu(item->submenu);
  461. else
  462. setcurrmenu(menu);
  463. previtem = item;
  464. } else if (menu->selected != item)
  465. menu->selected = item;
  466. drawmenu();
  467. }
  468. break;
  469. case ButtonRelease:
  470. getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
  471. if (menu != NULL && item != NULL) {
  472. if (item->label == NULL)
  473. break; /* ignore separators */
  474. if (item->submenu != NULL) {
  475. setcurrmenu(item->submenu);
  476. } else {
  477. printf("%s\n", item->output);
  478. cleanupexit();
  479. }
  480. drawmenu();
  481. } else {
  482. cleanupexit();
  483. }
  484. break;
  485. case LeaveNotify:
  486. currmenu->selected = NULL;
  487. drawmenu();
  488. break;
  489. }
  490. }
  491. }
  492. /* recursivelly free a pixmap */
  493. static void
  494. freewindow(struct Menu *menu)
  495. {
  496. struct Item *item;
  497. for (item = menu->list; item != NULL; item = item->next)
  498. if (item->submenu != NULL)
  499. freewindow(item->submenu);
  500. XFreePixmap(dpy, menu->pixmap);
  501. XDestroyWindow(dpy, menu->win);
  502. }
  503. /* cleanup and exit */
  504. static void
  505. cleanupexit(void)
  506. {
  507. freewindow(rootmenu);
  508. XFreeFont(dpy, dc.font);
  509. XFreeGC(dpy, dc.gc);
  510. XCloseDisplay(dpy);
  511. exit(0);
  512. }
  513. /* show usage */
  514. static void
  515. usage(void)
  516. {
  517. (void)fprintf(stderr, "usage: xmenu [-w] menuname\n");
  518. exit(1);
  519. }