You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

2769 lines
98 KiB

  1. // this code is not optimal in many ways, but works just nicely.
  2. // Licence: GNU GPL v3
  3. // Authors: Tomas Matejicek <www.slax.org>
  4. // Peter Munch-Ellingsen <www.peterme.net>
  5. const int VERSION_MAJOR = 4; // Major version, changes when breaking backwards compatability
  6. const int VERSION_MINOR = 5; // Minor version, changes when new functionality is added
  7. const int VERSION_PATCH = 4; // Patch version, changes when something is changed without changing deliberate functionality (eg. a bugfix or an optimisation)
  8. #define _GNU_SOURCE
  9. /* open and O_RDWR,O_CREAT */
  10. #include <fcntl.h>
  11. /* include X11 stuff */
  12. #include <X11/Xlib.h>
  13. /* include Imlib2 stuff */
  14. #include <Imlib2.h>
  15. /* basename include */
  16. #include <libgen.h>
  17. /* sprintf include */
  18. #include <stdio.h>
  19. /* strcpy include */
  20. #include <string.h>
  21. /* exit include */
  22. #include <stdlib.h>
  23. /* exec include */
  24. #include <unistd.h>
  25. /* long options include*/
  26. #include <getopt.h>
  27. /* cursor */
  28. #include <X11/cursorfont.h>
  29. /* X utils */
  30. #include <X11/Xutil.h>
  31. #include <X11/Xatom.h>
  32. /* parse commandline arguments */
  33. #include <ctype.h>
  34. /* one instance */
  35. #include <sys/file.h>
  36. #include <sys/stat.h>
  37. /* check stdin */
  38. #include <sys/poll.h>
  39. #include <errno.h>
  40. /* some globals for our window & X display */
  41. Display *disp;
  42. Window win;
  43. XVisualInfo vinfo;
  44. XSetWindowAttributes attr;
  45. int x11_fd;
  46. struct pollfd eventfds[2];
  47. XIM im;
  48. XIC ic;
  49. int screen;
  50. int screen_width;
  51. int screen_height;
  52. // Let's define a linked list node:
  53. typedef struct node
  54. {
  55. char title[256];
  56. char icon[256];
  57. char cmd[512];
  58. int hovered;
  59. int clicked;
  60. int hidden;
  61. int x;
  62. int y;
  63. struct node * next;
  64. } node_t;
  65. typedef struct button
  66. {
  67. char icon_normal[256];
  68. char icon_highlight[256];
  69. char cmd[512];
  70. int hovered;
  71. int clicked;
  72. int x;
  73. int y;
  74. int w;
  75. int h;
  76. struct button * next;
  77. } button_t;
  78. typedef struct shortcut
  79. {
  80. char *key;
  81. node_t *entry;
  82. struct shortcut *next;
  83. } shortcut_t;
  84. typedef struct keynode {
  85. char key[255];
  86. struct keynode * next;
  87. } keynode_t;
  88. typedef struct color {
  89. int r, g, b, a;
  90. } color_t;
  91. typedef struct percentable {
  92. int percent;
  93. int value;
  94. } percentable_t;
  95. int entries_count = 0;
  96. node_t * entries = NULL;
  97. button_t * buttons = NULL;
  98. shortcut_t * shortcuts = NULL;
  99. keynode_t * cmdline = NULL;
  100. enum exit_code {
  101. OKAY,
  102. ESCAPE = 0x20,
  103. RIGHTCLICK,
  104. VOIDCLICK,
  105. FOCUSLOST,
  106. INTERNALCMD,
  107. LOCKERROR = 0x40,
  108. ALLOCERROR,
  109. FONTERROR,
  110. CONFIGERROR,
  111. WINERROR,
  112. LOCALEERROR,
  113. INPUTMERROR,
  114. INPUTCERROR,
  115. POLLERROR
  116. };
  117. static struct option long_options[] =
  118. {
  119. {"tc", required_argument, 0, 1009},
  120. {"textcolor", required_argument, 0, 1009},
  121. {"pc", required_argument, 0, 1010},
  122. {"promptcolor", required_argument, 0, 1010},
  123. {"backgroundcolor", required_argument, 0, 1011},
  124. {"bc", required_argument, 0, 1011},
  125. {"hc", required_argument, 0, 1012},
  126. {"highlightcolor", required_argument, 0, 1012},
  127. {"name", required_argument, 0, 1013},
  128. {"config", required_argument, 0, 1014},
  129. {"bgfill", no_argument, 0, 1015},
  130. {"focuslostterminate", no_argument, 0, 1016},
  131. {"borderratio", required_argument, 0, 1017},
  132. {"sideborderratio", required_argument, 0, 1018},
  133. {"noscroll", no_argument, 0, 1019},
  134. {"iconvpadding", required_argument, 0, 1020},
  135. {"shadowcolor", required_argument, 0, 1021},
  136. {"sc", required_argument, 0, 1021},
  137. {"button", required_argument, 0, 'A'},
  138. {"textafter", no_argument, 0, 'a'},
  139. {"border", required_argument, 0, 'b'},
  140. {"sideborder", required_argument, 0, 'B'},
  141. {"center", no_argument, 0, 'C'},
  142. {"columns", required_argument, 0, 'c'},
  143. {"desktop", no_argument, 0, 'd'},
  144. {"hidemissing", no_argument, 0, 'e'},
  145. {"font", required_argument, 0, 'f'},
  146. {"promptfont", required_argument, 0, 'F'},
  147. {"background", required_argument, 0, 'g'},
  148. {"rootwindowbackground", no_argument, 0, 'G'},
  149. {"height", required_argument, 0, 'h'},
  150. {"help", no_argument, 0, 'H'},
  151. {"iconpadding", required_argument, 0, 'I'},
  152. {"input", required_argument, 0, 'i'},
  153. {"highlight", required_argument, 0, 'L'},
  154. {"leastmargin", required_argument, 0, 'l'},
  155. {"clearmemory", no_argument, 0, 'M'},
  156. {"multiple", no_argument, 0, 'm'},
  157. {"noprompt", no_argument, 0, 'n'},
  158. {"notitle", no_argument, 0, 'N'},
  159. {"outputonly", no_argument, 0, 'o'},
  160. {"textotherside", no_argument, 0, 'O'},
  161. {"prompt", required_argument, 0, 'p'},
  162. {"promptspacing", required_argument, 0, 'P'},
  163. {"dontquit", no_argument, 0, 'q'},
  164. {"reverse", no_argument, 0, 'R'},
  165. {"rows", required_argument, 0, 'r'},
  166. {"iconsize", required_argument, 0, 's'},
  167. {"selectonly", no_argument, 0, 'S'},
  168. {"textpadding", required_argument, 0, 'T'},
  169. {"voidclickterminate", no_argument, 0, 't'},
  170. {"shortcuts", required_argument, 0, 'U'},
  171. {"upsidedown", no_argument, 0, 'u'},
  172. {"leastvmargin", required_argument, 0, 'V'},
  173. {"version", no_argument, 0, 'v'},
  174. {"width", required_argument, 0, 'w'},
  175. {"windowed", no_argument, 0, 'W'},
  176. {"paddingswap", no_argument, 0, 'X'},
  177. {"xposition", required_argument, 0, 'x'},
  178. {"yposition", required_argument, 0, 'y'},
  179. {0, 0, 0, 0}
  180. };
  181. int icon_size = 48;
  182. int ucolumns = 0;
  183. int columns;
  184. int urows = 0;
  185. int rows;
  186. int column_margin = 0;
  187. int row_margin = 0;
  188. int icon_padding = 40;
  189. int icon_v_padding = -1;
  190. int text_padding = 10;
  191. int border;
  192. int side_border = 0;
  193. int border_ratio = 50;
  194. int side_border_ratio = 50;
  195. int cell_width;
  196. int cell_height;
  197. int font_height;
  198. int prompt_font_height;
  199. int use_root_img = 0;
  200. char commandline[10024];
  201. char commandlinetext[10024];
  202. int prompt_x;
  203. int prompt_y;
  204. int mouse_moves=0;
  205. char * background_file = "";
  206. char * highlight_file = "";
  207. char * input_file = "";
  208. int read_config = 0;
  209. FILE * input_source = NULL;
  210. char * prompt = "";
  211. char * font_name = "";
  212. char * prompt_font_name = "";
  213. char * program_name;
  214. int bg_fill = 0;
  215. int no_prompt = 0;
  216. int no_title = 0;
  217. int prompt_spacing = 48;
  218. int windowed = 0;
  219. int multiple_instances = 0;
  220. int uposx = 0;
  221. int uposy = 0;
  222. int force_reposition = 0;
  223. int uwidth = 0;
  224. int uheight = 0;
  225. percentable_t uborder = { .percent = -1, .value = 0 };
  226. percentable_t uside_border = { .percent = -1, .value = 0 };
  227. int void_click_terminate = 0;
  228. int focus_lost_terminate = 0;
  229. int dont_quit = 0;
  230. int reverse = 0;
  231. int output_only = 0;
  232. int select_only = 0;
  233. int text_after = 0;
  234. int text_other_side = 0;
  235. int clear_memory = 0;
  236. int upside_down = 0;
  237. int padding_swap = 0;
  238. int least_margin = 0;
  239. int least_v_margin = -1;
  240. int hide_missing = 0;
  241. int center_icons = 0;
  242. int noscroll = 0;
  243. int scrolled_past= 0;
  244. int hovered_entry = 0;
  245. color_t text_color = {.r = 255, .g = 255, .b = 255, .a = 255};
  246. color_t prompt_color = {.r = 255, .g = 255, .b = 255, .a = 255};
  247. color_t background_color = {.r = 46, .g = 52, .b = 64, .a = 255};
  248. color_t shadow_color = {.r = 0, .g = 0, .b = 0, .a = 30};
  249. int background_color_set = 0;
  250. color_t highlight_color = {.r = 255, .g = 255, .b = 255, .a = 50};
  251. #define MOUSE 1
  252. #define KEYBOARD 2
  253. int hoverset=MOUSE;
  254. int desktop_mode=0;
  255. int lock;
  256. /* areas to update */
  257. Imlib_Updates updates, current_update;
  258. /* our background image, rendered only once */
  259. Imlib_Image background = NULL;
  260. /* highlighting image (placed under icon on hover) */
  261. Imlib_Image highlight = NULL;
  262. /* image variable */
  263. Imlib_Image image = NULL;
  264. void calculate_percentage(int maxvalue, percentable_t* percentable)
  265. {
  266. if(percentable->percent != -1) {
  267. percentable->value = (maxvalue * percentable->percent) / 100;
  268. }
  269. }
  270. void recalc_cells()
  271. {
  272. int margined_cell_width, margined_cell_height;
  273. if (text_after){
  274. cell_width=icon_size+icon_padding*2;
  275. cell_height=icon_size+icon_v_padding*2;
  276. margined_cell_width=icon_size+icon_padding*2+least_margin;
  277. margined_cell_height=icon_size+icon_v_padding*2+least_v_margin;
  278. if(ucolumns == 0)
  279. ucolumns = 1;
  280. } else {
  281. cell_width=icon_size+icon_padding*2;
  282. cell_height=icon_size+icon_v_padding*2+font_height+text_padding;
  283. margined_cell_width=icon_size+icon_padding*2+least_margin;
  284. margined_cell_height=icon_size+icon_v_padding*2+font_height+text_padding+least_v_margin;
  285. }
  286. border = screen_width/10;
  287. if (uborder.value > 0) border = uborder.value;
  288. if (uborder.value == -1 && uborder.percent == -1){
  289. if (ucolumns == 0){
  290. border = 0;
  291. } else {
  292. side_border = (screen_width - (ucolumns * cell_width + (ucolumns - 1) * least_margin))/2;
  293. border = (screen_height - prompt_font_height - prompt_spacing - (urows * cell_height + (urows - 1) * least_v_margin))/2;
  294. }
  295. }
  296. if (uside_border.value == 0 && uside_border.percent == -1) side_border = border;
  297. if (uside_border.value > 0) side_border = uside_border.value;
  298. if (uside_border.value == -1 && uside_border.percent == -1){
  299. if (ucolumns == 0){
  300. side_border = 0;
  301. } else {
  302. side_border = (screen_width - (ucolumns * cell_width + (ucolumns - 1) * least_margin))/2;
  303. }
  304. }
  305. int usable_width;
  306. int usable_height;
  307. // These do while loops should run at most three times, it's just to avoid copying code
  308. do {
  309. usable_width = screen_width-side_border*2;
  310. usable_height = screen_height-border*2;
  311. usable_height = screen_height-border*2-prompt_spacing-prompt_font_height;
  312. // If the usable_width is too small, take some space away from the border
  313. if (usable_width < cell_width) {
  314. side_border = (screen_width - cell_width - 1)/2;
  315. } else if (usable_height < cell_height) {
  316. border = (screen_height - cell_height - prompt_spacing - prompt_font_height - 1)/2;
  317. }
  318. } while ((usable_width < cell_width && screen_width > cell_width) || (usable_height < cell_height && screen_height > cell_height));
  319. // just in case, make sure border is never negative
  320. if (border < 0) border = 0;
  321. if (side_border < 0) side_border = 0;
  322. // If columns were not manually overriden, calculate the most it can possibly contain
  323. if (ucolumns == 0){
  324. columns = usable_width/margined_cell_width;
  325. } else{
  326. columns = ucolumns;
  327. }
  328. if (urows == 0){
  329. rows = usable_height/margined_cell_height;
  330. } else{
  331. rows = urows;
  332. }
  333. // If we don't have space for a single column or row, force it.
  334. if (columns <= 0) {
  335. columns = 1;
  336. }
  337. if (rows <= 0 ) {
  338. rows = 1;
  339. }
  340. if (text_after){
  341. cell_width = (usable_width - least_margin*(columns - 1))/columns;
  342. }
  343. // The space between the icon tiles to fill all the space
  344. if (columns == 1){
  345. column_margin = (usable_width - cell_width*columns);
  346. } else {
  347. column_margin = (usable_width - cell_width*columns)/(columns - 1);
  348. }
  349. if (rows == 1){
  350. row_margin = (usable_height - cell_height*rows);
  351. } else {
  352. row_margin = (usable_height - cell_height*rows)/(rows - 1);
  353. }
  354. // These are kept in case manual positioning is reintroduced
  355. prompt_x = (side_border * side_border_ratio) / 50;
  356. prompt_y = (border * border_ratio) / 50;
  357. /*
  358. if(uside_border == 0){
  359. side_border = border;
  360. }*/
  361. }
  362. void restack()
  363. {
  364. if (desktop_mode) XLowerWindow(disp,win);
  365. else if (!windowed) XRaiseWindow(disp,win);
  366. }
  367. char* strncpyutf8(char* dst, const char* src, size_t num)
  368. {
  369. if(num)
  370. {
  371. size_t sizeSrc = strlen(src); // number of bytes not including null
  372. while(sizeSrc>num)
  373. {
  374. const char* lastByte = src + sizeSrc; // initially \0 at end
  375. while(lastByte-- > src) // test previous chars
  376. if((*lastByte & 0xC0) != 0x80) // utf8 start byte found
  377. break;
  378. sizeSrc = lastByte-src;
  379. }
  380. memcpy(dst, src, sizeSrc);
  381. dst[sizeSrc] = '\0';
  382. }
  383. return dst;
  384. }
  385. void arrange_positions()
  386. {
  387. node_t * current = entries;
  388. int i = 0;
  389. int j = 0;
  390. while (current != NULL)
  391. {
  392. if (current->hidden)
  393. {
  394. current->x = 0;
  395. current->y = 0;
  396. }
  397. else
  398. {
  399. int entries_last_line = entries_count - j * columns;
  400. if (center_icons && entries_last_line < columns) {
  401. int width = entries_last_line * cell_width + (entries_last_line - 1) * column_margin;
  402. int usable_width = screen_width - side_border * 2;
  403. int margin = usable_width - width;
  404. current->x = (side_border * side_border_ratio) / 50 + margin / 2 + i * (cell_width + column_margin);
  405. } else {
  406. current->x = (side_border * side_border_ratio) / 50 + i * (cell_width+column_margin);
  407. }
  408. current->y = (border * border_ratio) / 50 + prompt_font_height + prompt_spacing + (j - scrolled_past) * (cell_height+row_margin);
  409. if (upside_down) {
  410. current->y=screen_height - cell_height - current->y;
  411. }
  412. if (i == columns-1) {
  413. i = 0;
  414. j++;
  415. } else {
  416. i++;
  417. }
  418. }
  419. current = current->next;
  420. }
  421. }
  422. void push_key(char * key)
  423. {
  424. keynode_t * current = cmdline;
  425. if (current==NULL)
  426. {
  427. cmdline = malloc(sizeof(keynode_t));
  428. strcpy(cmdline->key,key);
  429. cmdline->next = NULL;
  430. return;
  431. }
  432. while (current->next != NULL) {
  433. current = current->next;
  434. }
  435. /* now we can add a new item */
  436. current->next = malloc(sizeof(keynode_t));
  437. strcpy(current->next->key,key);
  438. current->next->next = NULL;
  439. }
  440. void pop_key()
  441. {
  442. if (cmdline==NULL) return;
  443. // if there is only one item, remove it
  444. if (cmdline->next==NULL)
  445. {
  446. free(cmdline);
  447. cmdline=NULL;
  448. return;
  449. }
  450. keynode_t * current = cmdline;
  451. while (current->next->next != NULL) {
  452. current = current->next;
  453. }
  454. /* now we can remove last item */
  455. free(current->next);
  456. current->next=NULL;
  457. }
  458. void clear_entries(){
  459. node_t * current = entries;
  460. while (current != NULL) {
  461. node_t * last = current;
  462. if (clear_memory) {
  463. memset(last->title, 0, 256);
  464. memset(last->icon, 0, 256);
  465. memset(last->cmd, 0, 512);
  466. // Free the entire struct, except for it's next pointer
  467. memset(last, 0, sizeof(node_t) - sizeof(node_t *));
  468. }
  469. free(last);
  470. current = current->next;
  471. }
  472. entries = NULL;
  473. }
  474. void cleanup()
  475. {
  476. flock(lock, LOCK_UN | LOCK_NB);
  477. // destroy window, disconnect display, and exit
  478. XDestroyWindow(disp,win);
  479. XFlush(disp);
  480. XCloseDisplay(disp);
  481. if(input_source == stdin){
  482. int fd = fileno(stdin);
  483. int flags = fcntl(fd, F_GETFL, 0);
  484. flags &= ~O_NONBLOCK;
  485. fcntl(fd, F_SETFL, flags);
  486. fclose(input_source);
  487. }
  488. clear_entries();
  489. button_t * button = buttons;
  490. buttons = NULL;
  491. while (button != NULL) {
  492. button_t *last = button;
  493. button = button->next;
  494. free(last);
  495. }
  496. shortcut_t * shortcut = shortcuts;
  497. shortcuts = NULL;
  498. while (shortcut != NULL) {
  499. shortcut_t *last = shortcut;
  500. shortcut = shortcut->next;
  501. free(last->key);
  502. free(last);
  503. }
  504. }
  505. Imlib_Image load_image(char * icon) {
  506. Imlib_Load_Error load_error;
  507. Imlib_Image image = imlib_load_image_with_error_return(icon, &load_error);
  508. if(image) {
  509. imlib_context_set_image(image);
  510. imlib_free_image();
  511. } else {
  512. fprintf(stderr, "Could not load icon %s, Imlib failed with: ", icon);
  513. switch(load_error) {
  514. case IMLIB_LOAD_ERROR_NONE:
  515. fprintf(stderr, "IMLIB_LOAD_ERROR_NONE");
  516. break;
  517. case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST:
  518. fprintf(stderr, "IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST");
  519. break;
  520. case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY:
  521. fprintf(stderr, "IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY");
  522. break;
  523. case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ:
  524. fprintf(stderr, "IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ");
  525. break;
  526. case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT:
  527. fprintf(stderr, "IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT");
  528. break;
  529. case IMLIB_LOAD_ERROR_PATH_TOO_LONG:
  530. fprintf(stderr, "IMLIB_LOAD_ERROR_PATH_TOO_LONG");
  531. break;
  532. case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT:
  533. fprintf(stderr, "IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT");
  534. break;
  535. case IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY:
  536. fprintf(stderr, "IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY");
  537. break;
  538. case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE:
  539. fprintf(stderr, "IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE");
  540. break;
  541. case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS:
  542. fprintf(stderr, "IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS");
  543. break;
  544. case IMLIB_LOAD_ERROR_OUT_OF_MEMORY:
  545. fprintf(stderr, "IMLIB_LOAD_ERROR_OUT_OF_MEMORY");
  546. break;
  547. case IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS:
  548. fprintf(stderr, "IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS");
  549. break;
  550. case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE:
  551. fprintf(stderr, "IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE");
  552. break;
  553. case IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE:
  554. fprintf(stderr, "IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE");
  555. break;
  556. case IMLIB_LOAD_ERROR_UNKNOWN:
  557. fprintf(stderr, "IMLIB_LOAD_ERROR_UNKNOWN");
  558. break;
  559. }
  560. fprintf(stderr, "\n");
  561. /*
  562. cleanup();
  563. exit(1);*/
  564. }
  565. return image;
  566. }
  567. void push_entry(node_t * new_entry)//(char * title, char * icon, char * cmd, int x, int y)
  568. {
  569. node_t * current = entries;
  570. int hasicon = (strlen(new_entry->icon) != 0);
  571. /* Pre-load the image into the cache, this is done to check for error messages
  572. * If a user has more images then can be shown this might incur a performance hit */
  573. if (hasicon) {
  574. Imlib_Image image = load_image(new_entry->icon);
  575. if (image == NULL) {
  576. if (hide_missing) return;
  577. strcpy(new_entry->icon, "/usr/share/icons/hicolor/48x48/apps/xlunch_ghost.png");
  578. }
  579. }
  580. // Set default values for internal state
  581. new_entry->x=0;
  582. new_entry->y=0;
  583. new_entry->hidden=0;
  584. new_entry->hovered=0;
  585. new_entry->clicked=0;
  586. new_entry->next = NULL;
  587. shortcut_t *shortcut = shortcuts;
  588. while (shortcut != NULL) {
  589. if (shortcut->entry == NULL) {
  590. shortcut->entry = new_entry;
  591. break;
  592. }
  593. shortcut = shortcut->next;
  594. }
  595. // empty list, add first directly
  596. if (current==NULL)
  597. {
  598. entries = new_entry;
  599. entries->next = NULL;
  600. return;
  601. }
  602. // Otherwise determine position
  603. if(!reverse){
  604. // Go to end of list and add there
  605. while (current->next != NULL) {
  606. current = current->next;
  607. }
  608. current->next = new_entry;
  609. current->next->next = NULL;
  610. } else {
  611. // Add at head of list
  612. new_entry->next = entries;
  613. entries = new_entry;
  614. }
  615. entries_count++;
  616. }
  617. char *strtok_new(char * string, char const * delimiter){
  618. static char *source = NULL;
  619. char *p, *riturn = 0;
  620. if(string != NULL) source = string;
  621. if(source == NULL) return NULL;
  622. if((p = strpbrk (source, delimiter)) != NULL) {
  623. *p = 0;
  624. riturn = source;
  625. source = ++p;
  626. }
  627. return riturn;
  628. }
  629. char* concat(const char *s1, const char *s2)
  630. {
  631. char *result = malloc(strlen(s1)+strlen(s2)+1);//+1 for the zero-terminator
  632. if(result != 0) {
  633. strcpy(result, s1);
  634. strcat(result, s2);
  635. return result;
  636. }
  637. exit(ALLOCERROR);
  638. }
  639. FILE * determine_input_source(){
  640. FILE * fp;
  641. if(input_source == NULL) {
  642. char * homeconf = NULL;
  643. char * home = getenv("HOME");
  644. if (home!=NULL)
  645. {
  646. homeconf = concat(home,"/.config/xlunch/entries.dsv");
  647. }
  648. if (strlen(input_file)==0){
  649. fp = stdin;
  650. int flags;
  651. int fd = fileno(fp);
  652. flags = fcntl(fd, F_GETFL, 0);
  653. flags |= O_NONBLOCK;
  654. fcntl(fd, F_SETFL, flags);
  655. struct pollfd fds;
  656. fds.fd = 0; /* this is STDIN */
  657. fds.events = POLLIN;
  658. // Give poll a little timeout to make give the piping program some time
  659. if (poll(&fds, 1, 10) == 0){
  660. int flags = fcntl(fd, F_GETFL, 0);
  661. flags &= ~O_NONBLOCK;
  662. fcntl(fd, F_SETFL, flags);
  663. fclose(stdin);
  664. fp = fopen(homeconf, "rb");
  665. }
  666. } else {
  667. fp = fopen(input_file, "rb");
  668. }
  669. if (fp == NULL)
  670. {
  671. fprintf(stderr, "Error getting entries from %s.\nReverting back to system entries list.\n", strlen(input_file) == 0 ? "stdin" : input_file);
  672. input_file = "/etc/xlunch/entries.dsv";
  673. fp = fopen(input_file, "rb");
  674. if (fp == NULL)
  675. {
  676. fprintf(stderr, "Error opening entries file %s\n", input_file);
  677. fprintf(stderr, "You may need to create it. Icon file format is following:\n");
  678. fprintf(stderr, "title;icon_path;command\n");
  679. fprintf(stderr, "title;icon_path;command\n");
  680. fprintf(stderr, "title;icon_path;command\n");
  681. }
  682. }
  683. free(homeconf);
  684. } else {
  685. fp = input_source;
  686. }
  687. return fp;
  688. }
  689. int mouse_over_cell(node_t * cell, int index, int mouse_x, int mouse_y)
  690. {
  691. if (cell->hidden) return 0;
  692. if (index > scrolled_past*columns && index < scrolled_past*columns + rows*columns +1
  693. && mouse_x >= cell->x
  694. && mouse_x < cell->x+cell_width
  695. && mouse_y >= cell->y
  696. && mouse_y < cell->y+cell_height) return 1;
  697. else return 0;
  698. }
  699. int mouse_over_button(button_t * button, int mouse_x, int mouse_y)
  700. {
  701. int x = (button->x < 0 ? screen_width + button->x + 1 - button->w : button->x);
  702. int y = (button->y < 0 ? screen_height + button->y + 1 - button->h : button->y);
  703. if ( mouse_x >= x
  704. && mouse_x < x+button->w
  705. && mouse_y >= y
  706. && mouse_y < y+button->h) return 1;
  707. else return 0;
  708. }
  709. Pixmap GetRootPixmap(Display* display, Window *root)
  710. {
  711. Pixmap currentRootPixmap;
  712. Atom act_type;
  713. int act_format;
  714. unsigned long nitems, bytes_after;
  715. unsigned char *data = NULL;
  716. Atom _XROOTPMAP_ID;
  717. _XROOTPMAP_ID = XInternAtom(display, "_XROOTPMAP_ID", False);
  718. if (XGetWindowProperty(display, *root, _XROOTPMAP_ID, 0, 1, False,
  719. XA_PIXMAP, &act_type, &act_format, &nitems, &bytes_after,
  720. &data) == Success) {
  721. if (data) {
  722. currentRootPixmap = *((Pixmap *) data);
  723. XFree(data);
  724. }
  725. }
  726. return currentRootPixmap;
  727. }
  728. int get_root_image_to_imlib_data(DATA32 * direct)
  729. {
  730. int screen;
  731. Window root;
  732. Display* display;
  733. XWindowAttributes attrs;
  734. Pixmap bg;
  735. XImage* img;
  736. display = XOpenDisplay(NULL);
  737. if (display == NULL) return 0;
  738. screen = DefaultScreen(display);
  739. root = RootWindow(display, screen);
  740. XGetWindowAttributes(display, root, &attrs);
  741. bg = GetRootPixmap(display, &root);
  742. img = XGetImage(display, bg, 0, 0, attrs.width, attrs.height, ~0, ZPixmap);
  743. XFreePixmap(display, bg);
  744. if (!img) {
  745. XCloseDisplay(display);
  746. return 0;
  747. }
  748. unsigned long pixel;
  749. int x, y;
  750. for (y = 0; y < img->height; y++)
  751. {
  752. for (x = 0; x < img->width; x++)
  753. {
  754. pixel = XGetPixel(img,x,y);
  755. direct[y*img->width+x+0] = 0xffffffff&pixel;
  756. }
  757. }
  758. XDestroyImage(img);
  759. return 1;
  760. }
  761. void joincmdline()
  762. {
  763. strcpy(commandline,"");
  764. keynode_t * current = cmdline;
  765. while (current != NULL) {
  766. strcat(commandline,current->key);
  767. current = current->next;
  768. }
  769. }
  770. void joincmdlinetext()
  771. {
  772. if (no_prompt) return;
  773. if (strlen(prompt)==0) prompt="Run: ";
  774. strcpy(commandlinetext,prompt);
  775. keynode_t * current = cmdline;
  776. while (current != NULL) {
  777. strcat(commandlinetext,current->key);
  778. current = current->next;
  779. }
  780. strcat(commandlinetext,"_");
  781. }
  782. void set_scroll_level(int new_scroll) {
  783. if (!noscroll){
  784. if (new_scroll != scrolled_past) {
  785. scrolled_past = new_scroll;
  786. if (scrolled_past > (entries_count - 1)/columns - rows + 1) {
  787. scrolled_past = (entries_count - 1)/columns - rows + 1;
  788. }
  789. if (scrolled_past < 0) {
  790. scrolled_past = 0;
  791. }
  792. arrange_positions();
  793. updates = imlib_update_append_rect(updates, 0, 0, screen_width, screen_height);
  794. }
  795. }
  796. }
  797. void set_hover(int hovered, node_t * cell, int hover)
  798. {
  799. if (hover) hovered_entry = hovered;
  800. if (cell==NULL) return;
  801. if (cell->hidden) return;
  802. if (cell->hovered!=hover) updates = imlib_update_append_rect(updates, cell->x, cell->y, cell_width, cell_height);
  803. cell->hovered=hover;
  804. }
  805. void hover_entry(int entry){
  806. hoverset = KEYBOARD;
  807. int to_row = (entry+columns-1)/columns;
  808. int display_row = (hovered_entry-(scrolled_past*columns)+columns-1)/columns;
  809. if (entry>(columns*rows)+scrolled_past*columns || entry<=scrolled_past*columns) {
  810. set_scroll_level(to_row - display_row);
  811. }
  812. int j = 1;
  813. int max = scrolled_past*columns + columns*rows;
  814. max = (max > entries_count ? entries_count : max);
  815. int i = (entry < scrolled_past*columns + 1
  816. ? scrolled_past*columns + 1
  817. : (entry > max
  818. ? max
  819. : entry));
  820. node_t * current = entries;
  821. while (current != NULL) {
  822. if (j == i) set_hover(i, current, 1);
  823. else set_hover(i, current, 0);
  824. if (!current->hidden) j++;
  825. current = current->next;
  826. }
  827. }
  828. void filter_entries()
  829. {
  830. node_t * current = entries;
  831. entries_count = 0;
  832. while (current != NULL)
  833. {
  834. if (strlen(commandline)==0 || strcasestr(current->title,commandline)!=NULL || strcasestr(current->cmd,commandline)!=NULL){
  835. current->hidden=0;
  836. entries_count ++;
  837. } else {
  838. current->hidden=1;
  839. current->clicked=0;
  840. set_hover(0, current, 1);
  841. }
  842. current=current->next;
  843. }
  844. set_scroll_level(0);
  845. }
  846. int starts_with(const char *pre, const char *str)
  847. {
  848. size_t lenpre = strlen(pre),
  849. lenstr = strlen(str);
  850. return lenstr < lenpre ? 0 : strncmp(pre, str, lenpre) == 0;
  851. }
  852. void run_command(char * cmd_orig);
  853. void run_internal_command(char * cmd_orig) {
  854. int will_quit = 0;
  855. char * cmd = malloc(strlen(cmd_orig)+1);
  856. memcpy(cmd, cmd_orig, strlen(cmd_orig)+1);
  857. char *p = strtok( cmd, " ");
  858. if (strcmp(p, "scroll") == 0) {
  859. p = strtok( NULL, " ");
  860. if (strcmp(p, "top") == 0){
  861. set_scroll_level(0);
  862. } else if (strcmp(p, "bottom") == 0){
  863. set_scroll_level(entries_count);
  864. } else {
  865. if (p[0] == '+' || p[0] == '-')
  866. set_scroll_level(scrolled_past + atoi(p));
  867. else
  868. set_scroll_level(atoi(p));
  869. }
  870. } else if (strcmp(p, "hover") == 0) {
  871. if (hoverset != KEYBOARD) hovered_entry = 0;
  872. p = strtok( NULL, " ");
  873. int new_hover;
  874. if (strcmp(p, "start") == 0){
  875. new_hover = 1;
  876. } else if (strcmp(p, "end") == 0){
  877. new_hover = entries_count;
  878. } else {
  879. if (p[0] == '+' || p[0] == '-')
  880. new_hover = hovered_entry + atoi(p);
  881. else
  882. new_hover = atoi(p);
  883. }
  884. hover_entry(new_hover);
  885. } else if (strcmp(p, "exec") == 0) {
  886. int old_oo = output_only;
  887. output_only = 0;
  888. run_command(strtok(NULL, "\""));
  889. output_only = old_oo;
  890. } else if (strcmp(p, "print") == 0){
  891. printf("%s\n", strtok(NULL, "\""));
  892. } else if (strcmp(p, "quit") == 0) {
  893. will_quit = 1;
  894. }
  895. if (p != NULL) {
  896. p = strtok(NULL, "");
  897. if (p != NULL) {
  898. if (p[0] == ':') {
  899. run_internal_command(&p[1]);
  900. } else {
  901. fprintf(stderr, "Internal command \"%s\" supplied with extraneous parameters \"%s\"\n", cmd_orig, p);
  902. }
  903. }
  904. }
  905. free(cmd);
  906. if (will_quit) {
  907. cleanup();
  908. exit(INTERNALCMD);
  909. }
  910. }
  911. void reset_prompt()
  912. {
  913. while (cmdline != NULL) pop_key();
  914. joincmdline();
  915. joincmdlinetext();
  916. filter_entries();
  917. arrange_positions();
  918. node_t * current = entries;
  919. while (current != NULL) {
  920. set_hover(0, current, 0);
  921. current = current->next;
  922. }
  923. updates = imlib_update_append_rect(updates, 0, 0, screen_width, screen_height);
  924. }
  925. void run_command(char * cmd_orig)
  926. {
  927. char cmd[512];
  928. char *array[100] = {0};
  929. strcpy(cmd,cmd_orig);
  930. int isrecur = starts_with(":recur ", cmd_orig) || (strcmp(":recur", cmd_orig) == 0);
  931. if(isrecur) {
  932. // split arguments into pieces
  933. int i = 0;
  934. // If we recur (ie. start xlunch again) run the command that was used to run xlunch
  935. array[0] = program_name;
  936. // Blindly consume the first token which should be "recur"
  937. char *p = strtok (cmd, " ");
  938. p = strtok (NULL, " ");
  939. while (p != NULL)
  940. {
  941. array[++i]=p;
  942. p = strtok (NULL, " ");
  943. if (i>=99) break;
  944. }
  945. } else {
  946. if (cmd_orig[0] == ':') {
  947. run_internal_command((char *)(cmd_orig + sizeof(char)));
  948. return;
  949. }
  950. if(output_only){
  951. fprintf(stdout, "%s\n", cmd_orig);
  952. if(!dont_quit){
  953. cleanup();
  954. exit(OKAY);
  955. } else {
  956. reset_prompt();
  957. return;
  958. }
  959. }
  960. array[0] = "/bin/bash";
  961. array[1] = "-c";
  962. array[2] = cmd_orig;
  963. array[3] = NULL;
  964. }
  965. restack();
  966. if (dont_quit)
  967. {
  968. pid_t pid=fork();
  969. if (pid==0) // child process
  970. {
  971. if (!multiple_instances) close(lock);
  972. printf("Forking command: %s\n",cmd);
  973. int err;
  974. err = execvp(array[0],array);
  975. fprintf(stderr,"Error forking %s : %d\n",cmd,err);
  976. exit(OKAY);
  977. }
  978. else if (pid<0) // error forking
  979. {
  980. fprintf(stderr,"Error running %s\n",cmd);
  981. }
  982. else // parent process
  983. {
  984. reset_prompt();
  985. }
  986. }
  987. else
  988. {
  989. cleanup();
  990. printf("Running command: %s\n",cmd);
  991. int err;
  992. err = execvp(array[0],array);
  993. fprintf(stderr,"Error running %s : %d\n",cmd, err);
  994. exit(OKAY);
  995. }
  996. }
  997. int parse_entries()
  998. {
  999. int changed = 0; // if the list of elements have changed or not
  1000. int parsing = 0; // section currently being read
  1001. int position = 0; // position in the current entry
  1002. int leading_space = 0;
  1003. int line = 1; // line count for error messages
  1004. int readstatus;
  1005. struct pollfd fds;
  1006. fds.fd = fileno(input_source);
  1007. fds.events = POLLIN;
  1008. node_t * current_entry = NULL;
  1009. char internal_command[128];
  1010. while(poll(&fds, 1, 0)) {
  1011. char b;
  1012. readstatus = read(fds.fd, &b, 1);
  1013. if(readstatus <= 0){
  1014. break;
  1015. }
  1016. if(parsing == 0 && position == leading_space){
  1017. if(b != ':' && b != '#') {
  1018. current_entry = malloc(sizeof(node_t));
  1019. } else {
  1020. current_entry = NULL;
  1021. if(b == '#'){
  1022. parsing = -1;
  1023. }
  1024. }
  1025. }
  1026. if (current_entry == NULL) {
  1027. if (b == '\n') b = '\0';
  1028. if (position > leading_space && parsing != -1)
  1029. internal_command[position-leading_space-1] = b;
  1030. } else {
  1031. if(b == '\0') {
  1032. fprintf(stderr, "Null-byte encountered while reading entries at line %d. Aborting.\n", line);
  1033. }
  1034. // Check for semi-colons only for the first two elements to support bash commands with semi-colons in them
  1035. if(b == ';' && parsing != 2) {
  1036. b = '\0';
  1037. } else if (b == '\n') {
  1038. line++;
  1039. b = '\0';
  1040. if(parsing == 0){
  1041. clear_entries();
  1042. changed = 1;
  1043. continue;
  1044. }
  1045. }
  1046. if(b == ' ' && position <= leading_space) leading_space++;
  1047. if(b != ' ' && leading_space > 0) leading_space = -leading_space;
  1048. switch(parsing){
  1049. case 0:
  1050. if (leading_space <= 0)
  1051. current_entry->title[position+leading_space] = b;
  1052. break;
  1053. case 1:
  1054. current_entry->icon[position] = b;
  1055. break;
  1056. case 2:
  1057. current_entry->cmd[position] = b;
  1058. break;
  1059. }
  1060. }
  1061. position++;
  1062. if(b == '\0') {
  1063. position = 0;
  1064. leading_space = 0;
  1065. if(parsing == 2 || current_entry == NULL) {
  1066. if(current_entry != NULL) {
  1067. push_entry(current_entry);
  1068. changed = 1;
  1069. } else {
  1070. if (parsing != -1)
  1071. run_internal_command(internal_command);
  1072. }
  1073. parsing = 0;
  1074. } else {
  1075. parsing++;
  1076. }
  1077. }
  1078. if(current_entry != NULL) {
  1079. int maxlen = (parsing == 2 ? 511 : 255);
  1080. if(position == maxlen){
  1081. fprintf(stderr, "Entry too long on line %d, maximum length is %d characters!\n", line, maxlen);
  1082. break;
  1083. }
  1084. }
  1085. }
  1086. if(readstatus == 0){
  1087. if(parsing == 2){
  1088. current_entry->cmd[position]='\0';
  1089. push_entry(current_entry);
  1090. changed = 1;
  1091. }
  1092. close(fds.fd);
  1093. input_source = NULL;
  1094. }
  1095. if(changed) {
  1096. filter_entries();
  1097. arrange_positions();
  1098. }
  1099. return changed;
  1100. }
  1101. void parse_button(char *button_spec) {
  1102. int parsing = 0; // section currently being read
  1103. int position = 0; // position in the current entry
  1104. int i = 0;
  1105. button_t *new_button = malloc(sizeof(button_t));
  1106. char b = button_spec[0];
  1107. char x[256];
  1108. char y[256];
  1109. while (b != '\0') {
  1110. if ((b == ';' && parsing != 4) || (parsing == 2 && b == ',') ) {
  1111. b = '\0';
  1112. }
  1113. switch(parsing){
  1114. case 0:
  1115. new_button->icon_normal[position] = b;
  1116. break;
  1117. case 1:
  1118. new_button->icon_highlight[position] = b;
  1119. break;
  1120. case 2:
  1121. x[position] = b;
  1122. break;
  1123. case 3:
  1124. y[position] = b;
  1125. break;
  1126. case 4:
  1127. new_button->cmd[position] = b;
  1128. break;
  1129. }
  1130. position ++;
  1131. if(b == '\0') {
  1132. position = 0;
  1133. parsing++;
  1134. }
  1135. int maxlen = (parsing == 4 ? 511 : 255);
  1136. if(position == maxlen){
  1137. fprintf(stderr, "Entry too long, maximum length is %d characters!\n", maxlen);
  1138. break;
  1139. }
  1140. i++;
  1141. b = button_spec[i];
  1142. }
  1143. if (x[0] == '-') {
  1144. new_button->x = atoi(x)-1;
  1145. } else {
  1146. new_button->x = atoi(x);
  1147. }
  1148. if (y[0] == '-') {
  1149. new_button->y = atoi(y)-1;
  1150. } else {
  1151. new_button->y = atoi(y);
  1152. }
  1153. imlib_context_set_image(imlib_load_image(new_button->icon_normal));
  1154. new_button->w = imlib_image_get_width();
  1155. new_button->h = imlib_image_get_height();
  1156. imlib_free_image();
  1157. new_button->cmd[position] = '\0';
  1158. new_button->next = buttons;
  1159. buttons = new_button;
  1160. }
  1161. void set_clicked(node_t * cell, int clicked)
  1162. {
  1163. if (cell==NULL) return;
  1164. if (cell->hidden) return;
  1165. if (cell->clicked!=clicked) updates = imlib_update_append_rect(updates, cell->x, cell->y, cell_width, cell_height);
  1166. cell->clicked=clicked;
  1167. }
  1168. Imlib_Font load_default_font(){
  1169. // No font was specified, try to load some default options as a fallback
  1170. Imlib_Font font;
  1171. font = imlib_load_font("OpenSans-Regular/10");
  1172. if (!font) font = imlib_load_font("DejaVuSans/10");
  1173. return font;
  1174. }
  1175. int get_font_height(Imlib_Font font){
  1176. imlib_context_set_font(font);
  1177. // maximum font descent is relative to baseline (ie. negative)
  1178. int height = imlib_get_maximum_font_ascent() - imlib_get_maximum_font_descent();
  1179. imlib_free_font();
  1180. return height;
  1181. }
  1182. Imlib_Font load_font()
  1183. {
  1184. Imlib_Font font;
  1185. if (strlen(font_name) == 0){
  1186. font = load_default_font();
  1187. } else {
  1188. font = imlib_load_font(font_name);
  1189. }
  1190. if (font == NULL) {
  1191. fprintf(stderr, "Font %s could not be loaded! Please specify one with -f parameter\n", font_name);
  1192. exit(FONTERROR);
  1193. }
  1194. if (!no_title)
  1195. font_height = get_font_height(font);
  1196. return font;
  1197. }
  1198. Imlib_Font load_prompt_font()
  1199. {
  1200. Imlib_Font font;
  1201. if (strlen(prompt_font_name) == 0){
  1202. if (strlen(font_name) == 0){
  1203. font = load_default_font();
  1204. } else {
  1205. font = imlib_load_font(font_name);
  1206. }
  1207. } else {
  1208. font = imlib_load_font(prompt_font_name);
  1209. }
  1210. if (font == NULL) {
  1211. fprintf(stderr, "Prompt font %s could not be loaded! Please specify one with -F parameter\n", prompt_font_name);
  1212. exit(FONTERROR);
  1213. }
  1214. if (!no_prompt)
  1215. prompt_font_height = get_font_height(font);
  1216. return font;
  1217. }
  1218. // set background image for desktop, optionally copy it from root window,
  1219. // and set background for highlighting items
  1220. //
  1221. void update_background_images()
  1222. {
  1223. /* reset images if exist */
  1224. if (background)
  1225. {
  1226. imlib_context_set_image(background);
  1227. imlib_free_image();
  1228. }
  1229. if (highlight)
  1230. {
  1231. imlib_context_set_image(highlight);
  1232. imlib_free_image();
  1233. }
  1234. // load highlighting image from a file
  1235. if (strlen(highlight_file)>0)
  1236. highlight=imlib_load_image(highlight_file);
  1237. /* fill the window background */
  1238. background = imlib_create_image(screen_width, screen_height);
  1239. // When xlunch is launched and there is another full screen window 'background' was NULL
  1240. if(background) {
  1241. imlib_context_set_image(background);
  1242. imlib_image_set_has_alpha(1);
  1243. imlib_image_clear();
  1244. if (use_root_img) {
  1245. DATA32 * direct = imlib_image_get_data();
  1246. int ok = get_root_image_to_imlib_data(direct);
  1247. if (ok) {
  1248. imlib_image_put_back_data(direct);
  1249. imlib_context_set_color(background_color.r, background_color.g, background_color.b, background_color.a);
  1250. imlib_context_set_blend(1);
  1251. imlib_image_fill_rectangle(0,0, screen_width, screen_height);
  1252. imlib_context_set_blend(0);
  1253. }
  1254. } else{ // load background from file
  1255. if (strlen(background_file)>0) {
  1256. image = imlib_load_image(background_file);
  1257. if (image) {
  1258. imlib_context_set_image(image);
  1259. int w = imlib_image_get_width();
  1260. int h = imlib_image_get_height();
  1261. imlib_context_set_image(background);
  1262. imlib_context_set_color(background_color.r, background_color.g, background_color.b, background_color.a);
  1263. imlib_context_set_blend(1);
  1264. // Those are our source coordinates if we use just scale
  1265. // this would give the same result as feh --bg-scale
  1266. int imx=0, imy=0, imw=w, imh=h;
  1267. // But we do not want to use scale, rather we use fill to keep aspect ratio
  1268. // It gives the same result as feh --bg-fill
  1269. if (bg_fill) {
  1270. if ( (double) (w/h) < (double) (screen_width/screen_height) )
  1271. {
  1272. imw = (int) w;
  1273. imh = (int) (screen_height * w / screen_width);
  1274. imx = 0;
  1275. imy = (int) ((h - imh) / 2);
  1276. }
  1277. else
  1278. {
  1279. imw = (int) (screen_width * h / screen_height);
  1280. imh = (int) h;
  1281. imx = (int) ((w - imw) / 2);
  1282. imy = 0;
  1283. }
  1284. }
  1285. imlib_blend_image_onto_image(image, 1, imx, imy, imw, imh, 0, 0, screen_width, screen_height);
  1286. imlib_image_fill_rectangle(0,0, screen_width, screen_height);
  1287. imlib_context_set_blend(0);
  1288. imlib_context_set_image(image);
  1289. imlib_free_image();
  1290. }
  1291. } else {
  1292. imlib_context_set_color(background_color.r, background_color.g, background_color.b, background_color.a);
  1293. imlib_image_fill_rectangle(0,0, screen_width, screen_height);
  1294. }
  1295. }
  1296. }
  1297. }
  1298. void draw_text_with_shadow(int posx, int posy, char * text, color_t color) {
  1299. imlib_context_set_color(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a);
  1300. imlib_text_draw(posx +1, posy +1, text);
  1301. imlib_text_draw(posx +1, posy +2, text);
  1302. imlib_text_draw(posx +2, posy +2, text);
  1303. imlib_context_set_color(color.r, color.g, color.b, color.a);
  1304. imlib_text_draw(posx, posy, text);
  1305. }
  1306. void handle_option(int c, char *optarg);
  1307. void parse_config(FILE *input) {
  1308. int readstatus;
  1309. int position = 0;
  1310. int size = 0;
  1311. int eol = 0;
  1312. int fileline = 1;
  1313. char *optarg = NULL;
  1314. char matching[(sizeof(long_options)/sizeof(struct option)) - 1];
  1315. char *entries_word = "entries";
  1316. int matching_entries = 1;
  1317. int matched = '?';
  1318. int comment = 0;
  1319. memset(matching, 1, sizeof(long_options)/sizeof(struct option) - 1);
  1320. struct pollfd fds;
  1321. fds.fd = fileno(input);
  1322. fds.events = POLLIN;
  1323. node_t * current_entry;
  1324. while(poll(&fds, 1, 0)) {
  1325. char b;
  1326. readstatus = read(fds.fd, &b, 1);
  1327. if(readstatus <= 0){
  1328. break;
  1329. }
  1330. if((b == ':' && optarg == NULL) || b == '\n') {
  1331. if (b == '\n') eol = 1;
  1332. b = '\0';
  1333. }
  1334. if(b == '#' && optarg == NULL && position == 0) comment = 1;
  1335. if(comment == 1 && eol != 1) continue;
  1336. if(b == ' ' && position == 0) continue;
  1337. if(optarg == NULL) {
  1338. for(int i = 0; i < sizeof(long_options)/sizeof(struct option) - 1; i++) {
  1339. if (long_options[i].name[position] != b){
  1340. matching[i] = 0;
  1341. }
  1342. }
  1343. if (entries_word[position] != b && eol != 1) {
  1344. matching_entries = 0;
  1345. }
  1346. if(b == '\0') {
  1347. for(int i = 0; i < sizeof(long_options)/sizeof(struct option) - 1; i++) {
  1348. if (matching[i] == 1) {
  1349. optarg = malloc(1);
  1350. position = -1;
  1351. matched = long_options[i].val;
  1352. }
  1353. }
  1354. }
  1355. } else {
  1356. optarg = realloc(optarg, ++size);
  1357. optarg[position] = b;
  1358. }
  1359. position++;
  1360. if(eol == 1) {
  1361. if(position != 1) {
  1362. if(matching_entries){
  1363. input_source = input;
  1364. break;
  1365. } else {
  1366. if(matched == '?') {
  1367. fprintf(stderr, "Got unknown option in config file on line %d\n", fileline);
  1368. }
  1369. handle_option(matched, optarg);
  1370. }
  1371. }
  1372. position = 0;
  1373. size = 0;
  1374. eol = 0;
  1375. comment = 0;
  1376. matching_entries = 1;
  1377. memset(matching, 1, sizeof(long_options)/sizeof(struct option) - 1);
  1378. if(optarg != NULL){
  1379. //free(optarg);
  1380. optarg = NULL;
  1381. }
  1382. matched = '?';
  1383. fileline++;
  1384. }
  1385. }
  1386. if(position > 1) {
  1387. if(matching_entries){
  1388. input_source = input;
  1389. } else {
  1390. if (matched == '?') {
  1391. for(int i = 0; i < sizeof(long_options)/sizeof(struct option) - 1; i++) {
  1392. if (matching[i] == 1) {
  1393. matched = long_options[i].val;
  1394. break;
  1395. }
  1396. }
  1397. }
  1398. if(matched == '?') {
  1399. fprintf(stderr, "Got unknown option in config file on line %d\n", fileline);
  1400. }
  1401. handle_option(matched, optarg);
  1402. }
  1403. }
  1404. if(input_source != input)
  1405. close(fds.fd);
  1406. }
  1407. void handle_option(int c, char *optarg) {
  1408. switch (c) {
  1409. case 'v':
  1410. fprintf(stderr, "xlunch graphical program launcher, version %d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
  1411. exit(OKAY);
  1412. case 'd':
  1413. desktop_mode = 1;
  1414. break;
  1415. case 'G':
  1416. use_root_img = 1;
  1417. if(background_color_set == 0){
  1418. background_color.r = 0;
  1419. background_color.g = 0;
  1420. background_color.b = 0;
  1421. background_color.a = 100;
  1422. }
  1423. break;
  1424. case 'n':
  1425. no_prompt = 1;
  1426. prompt_spacing = 0;
  1427. prompt_font_height = 0;
  1428. break;
  1429. case 'N':
  1430. no_title = 1;
  1431. font_height = 0;
  1432. text_padding = 0;
  1433. break;
  1434. case 'g':
  1435. background_file = optarg;
  1436. if(background_color_set == 0){
  1437. background_color.r = 0;
  1438. background_color.g = 0;
  1439. background_color.b = 0;
  1440. background_color.a = 100;
  1441. }
  1442. break;
  1443. case 'L':
  1444. highlight_file = optarg;
  1445. break;
  1446. case 'I':
  1447. icon_padding = atoi(optarg);
  1448. break;
  1449. case 1020:
  1450. icon_v_padding = atoi(optarg);
  1451. break;
  1452. case 'T':
  1453. text_padding = atoi(optarg);
  1454. break;
  1455. case 'c':
  1456. ucolumns = atoi(optarg);
  1457. break;
  1458. case 'r':
  1459. urows = atoi(optarg);
  1460. break;
  1461. case 'b':
  1462. if(strcmp(optarg, "auto") == 0){
  1463. uborder.value = -1;
  1464. } else {
  1465. if (optarg[strlen(optarg)-1] == '%') {
  1466. uborder.percent = atoi(optarg);
  1467. } else {
  1468. uborder.value = atoi(optarg);
  1469. }
  1470. }
  1471. break;
  1472. case 'B':
  1473. if(strcmp(optarg, "auto") == 0){
  1474. uside_border.value = -1;
  1475. } else {
  1476. if (optarg[strlen(optarg)-1] == '%') {
  1477. uside_border.percent = atoi(optarg);
  1478. } else {
  1479. uside_border.value = atoi(optarg);
  1480. }
  1481. }
  1482. break;
  1483. case 'P':
  1484. prompt_spacing = atoi(optarg);
  1485. break;
  1486. case 's':
  1487. icon_size = atoi(optarg);
  1488. break;
  1489. case 'i':
  1490. input_file = optarg;
  1491. break;
  1492. case 'W':
  1493. windowed = 1;
  1494. break;
  1495. case 'p':
  1496. prompt = optarg;
  1497. break;
  1498. case 'f':
  1499. font_name = optarg;
  1500. break;
  1501. case 'F':
  1502. prompt_font_name = optarg;
  1503. break;
  1504. case 'm':
  1505. multiple_instances = 1;
  1506. break;
  1507. case 't':
  1508. void_click_terminate = 1;
  1509. break;
  1510. case 'x':
  1511. uposx = atoi(optarg);
  1512. force_reposition = 1;
  1513. break;
  1514. case 'y':
  1515. uposy = atoi(optarg);
  1516. force_reposition = 1;
  1517. break;
  1518. case 'w':
  1519. uwidth = atoi(optarg);
  1520. break;
  1521. case 'h':
  1522. uheight = atoi(optarg);
  1523. break;
  1524. case 'o':
  1525. output_only = 1;
  1526. break;
  1527. case 'S':
  1528. select_only = 1;
  1529. break;
  1530. case 1009:
  1531. sscanf(optarg, "%02x%02x%02x%02x", &text_color.r, &text_color.g, &text_color.b, &text_color.a);
  1532. break;
  1533. case 1010:
  1534. sscanf(optarg, "%02x%02x%02x%02x", &prompt_color.r, &prompt_color.g, &prompt_color.b, &prompt_color.a);
  1535. break;
  1536. case 1011:
  1537. sscanf(optarg, "%02x%02x%02x%02x", &background_color.r, &background_color.g, &background_color.b, &background_color.a);
  1538. background_color_set = 1;
  1539. break;
  1540. case 1021:
  1541. sscanf(optarg, "%02x%02x%02x%02x", &shadow_color.r, &shadow_color.g, &shadow_color.b, &shadow_color.a);
  1542. break;
  1543. case 1012:
  1544. sscanf(optarg, "%02x%02x%02x%02x", &highlight_color.r, &highlight_color.g, &highlight_color.b, &highlight_color.a);
  1545. break;
  1546. case 'a':
  1547. text_after = 1;
  1548. break;
  1549. case 1013:
  1550. program_name = optarg;
  1551. break;
  1552. case 'q':
  1553. dont_quit = 1;
  1554. break;
  1555. case 'R':
  1556. reverse = 1;
  1557. break;
  1558. case 'O':
  1559. text_other_side = 1;
  1560. break;
  1561. case 'M':
  1562. clear_memory = 1;
  1563. break;
  1564. case 'u':
  1565. upside_down = 1;
  1566. break;
  1567. case 'X':
  1568. padding_swap = 1;
  1569. break;
  1570. case 'l':
  1571. least_margin = atoi(optarg);
  1572. break;
  1573. case 'V':
  1574. least_v_margin = atoi(optarg);
  1575. break;
  1576. case 'C':
  1577. center_icons = 1;
  1578. break;
  1579. case 'e':
  1580. hide_missing = 1;
  1581. break;
  1582. case 'A':
  1583. parse_button(optarg);
  1584. break;
  1585. case 'U': ;
  1586. unsigned char lb = optarg[0];
  1587. int i = 0;
  1588. shortcut_t *current_shortcut = NULL;
  1589. while (lb != '\0') {
  1590. if ( current_shortcut == NULL ) {
  1591. current_shortcut = malloc(sizeof(shortcut_t));
  1592. } else {
  1593. current_shortcut->next = malloc(sizeof(shortcut_t));
  1594. current_shortcut = current_shortcut->next;
  1595. }
  1596. if ( shortcuts == NULL)
  1597. shortcuts = current_shortcut;
  1598. current_shortcut->entry = NULL;
  1599. current_shortcut->next = NULL;
  1600. if (( lb & 0x80 ) == 0 ) { // lead bit is zero, must be a single ascii
  1601. current_shortcut->key = malloc(sizeof(char));
  1602. memcpy(current_shortcut->key, &optarg[i], sizeof(char));
  1603. i+=1;
  1604. } else if (( lb & 0xE0 ) == 0xC0 ) { // 110x xxxx
  1605. current_shortcut->key = malloc(sizeof(char)*2);
  1606. memcpy(current_shortcut->key, &optarg[i], sizeof(char)*2);
  1607. i+=2;
  1608. } else if (( lb & 0xF0 ) == 0xE0 ) { // 1110 xxxx
  1609. current_shortcut->key = malloc(sizeof(char)*3);
  1610. memcpy(current_shortcut->key, &optarg[i], sizeof(char)*3);
  1611. i+=3;
  1612. } else if (( lb & 0xF8 ) == 0xF0 ) { // 1111 0xxx
  1613. current_shortcut->key = malloc(sizeof(char)*4);
  1614. memcpy(current_shortcut->key, &optarg[i], sizeof(char)*4);
  1615. i+=4;
  1616. } else {
  1617. printf( "Unrecognized lead byte in shortcut (%02x)\n", lb );
  1618. }
  1619. lb = optarg[i];
  1620. }
  1621. break;
  1622. case 1014:
  1623. parse_config(fopen(optarg, "rb"));
  1624. read_config = 1;
  1625. break;
  1626. case 1015:
  1627. bg_fill = 1;
  1628. break;
  1629. case 1016:
  1630. focus_lost_terminate = 1;
  1631. break;
  1632. case 1017:
  1633. border_ratio = atoi(optarg);
  1634. break;
  1635. case 1018:
  1636. side_border_ratio = atoi(optarg);
  1637. break;
  1638. case 1019:
  1639. noscroll = 1;
  1640. break;
  1641. case '?':
  1642. fprintf(stderr, "See --help for usage documentation\n");
  1643. exit(CONFIGERROR);
  1644. break;
  1645. case 'H':
  1646. fprintf (stderr,"usage: xlunch [options]\n"
  1647. " xlunch is a program launcher/option selector similar to dmenu or rofi.\n"
  1648. " By default it launches in full-screen mode and terminates after a selection is made,\n"
  1649. " it is also possible to close xlunch by pressing Esc or the right mouse button.\n"
  1650. " Some options changes this behaviour, the most notable being the desktop mode switch:\n"
  1651. " -d, --desktop Desktop mode, always keep the launcher at\n"
  1652. " background(behind other windows), and ignore ESC\n"
  1653. " and right mouse click. Combined with --dontquit\n"
  1654. " xlunch never exits (behaviour of --desktop from\n"
  1655. " previous versions).\n"
  1656. " Functional options:\n"
  1657. " --config [file] Reads configuration options from a file. The\n"
  1658. " options are the same as the long options\n"
  1659. " specified here. Options that take a value must\n"
  1660. " have a colon (':') before it's option. It is\n"
  1661. " also possible to pass the entries along with the\n"
  1662. " configuration file by using \"entries:\"\n"
  1663. " followed by a newline and the regular contents\n"
  1664. " of an input file\n"
  1665. " -v, --version Returns the version of xlunch\n"
  1666. " -H, --help Shows this help message\n"
  1667. " --name POSIX-esque way to specify the first part of\n"
  1668. " WM_CLASS (default: environment variable \n"
  1669. " RESOURCE_NAME or argv[0])\n"
  1670. " -N, --notitle Do not display any titles under/around icons\n"
  1671. " -n, --noprompt Hides the prompt, only allowing selection\n"
  1672. " by icon\n"
  1673. " -o, --outputonly Do not run the selected entry, only output it\n"
  1674. " to stdout\n"
  1675. " -S, --selectonly Only allow an actual entry and not free-typed\n"
  1676. " commands. Nice for scripting.\n"
  1677. " -i, --input [file] File to read entries from, defaults to stdin\n"
  1678. " if data is available otherwise it reads from\n"
  1679. " $HOME/.config/xlunch/entries.dsv or\n"
  1680. " /etc/xlunch/entries.dsv\n"
  1681. " -m, --multiple Allow multiple instances running\n"
  1682. " -t, --voidclickterminate Clicking anywhere that's not an entry terminates\n"
  1683. " xlunch, practical for touch screens.\n"
  1684. " --focuslostterminate When the window loses focus xlunch will quit,\n"
  1685. " practical for menus\n"
  1686. " -q, --dontquit When an option is selected, don't close xlunch.\n"
  1687. " Combined with --desktop xlunch\n"
  1688. " never exits (behaviour of --desktop from\n"
  1689. " previous versions).\n"
  1690. " -R, --reverse All entries in xlunch as reversly ordered.\n"
  1691. " -W, --windowed Start in windowed mode\n"
  1692. " -M, --clearmemory Set the memory of each entry to null before\n"
  1693. " exiting. Used for passing sensitive information\n"
  1694. " through xlunch.\n"
  1695. " -U, --shortcuts [shortcuts] Sets shortcuts for the entries, 'shortcuts' is a\n"
  1696. " string of UTF-8 characters to use sequentially\n"
  1697. " for the entries provided.\n"
  1698. " -A, --button [button] Adds a button to the window. The argument\n"
  1699. " \"button\" is a semicolon-separated list on the\n"
  1700. " form \"<icon>;<highlight icon>;<x>,<y>;<command>\"\n"
  1701. " If x or y is negative positioning is relative\n"
  1702. " to the other side of the screen.\n"
  1703. " --noscroll Disable scroll in xlunch. Ignore entries\n"
  1704. " that can't fit the screen.\n\n"
  1705. " Multi monitor setup: xlunch cannot detect your output monitors, it sees your monitors\n"
  1706. " as a big single screen. You can customize this manually by setting windowed mode and\n"
  1707. " providing the top/left coordinates and width/height of your monitor screen which\n"
  1708. " effectively positions xlunch on the desired monitor. Use the following options:\n"
  1709. " -x, --xposition [i] The x coordinate of the launcher window\n"
  1710. " Use negative number to align from right\n"
  1711. " -y, --yposition [i] The y coordinate of the launcher window\n"
  1712. " Use negative number to align from bottom\n"
  1713. " -w, --width [i] The width of the launcher window\n"
  1714. " -h, --height [i] The height of the launcher window\n\n"
  1715. " Style options:\n"
  1716. " -p, --prompt [text] The prompt asking for input (default: \"Run: \")\n"
  1717. " -f, --font [name] Font name including size after slash (default:\n"
  1718. " \"OpenSans-Regular/10\" and \"DejaVuSans/10\")\n"
  1719. " -F, --promptfont [name] Font to use for the prompt\n"
  1720. " (default: same as --font)\n"
  1721. " -G, --rootwindowbackground Use root windows background image\n"
  1722. " -g, --background [file] Image to set as background (jpg/png). NOTE: the\n"
  1723. " background color will be drawn over this image\n"
  1724. " so use a fully transparent background color if\n"
  1725. " the image should be drawn as-is.\n"
  1726. " --bgfill Makes the background keep aspect ratio\n"
  1727. " while stretching\n"
  1728. " -L, --highlight [file] Image set as highlighting under selected icon\n"
  1729. " (jpg/png)\n"
  1730. " -I, --iconpadding [i] Padding around icons (default: 10)\n"
  1731. " --iconvpadding [i] Vertical padding around icons (default: same as\n"
  1732. " iconpadding)\n"
  1733. " -T, --textpadding [i] Padding around entry titles (default: 10)\n"
  1734. " -c, --columns [i] Number of columns to show (without this the max\n"
  1735. " amount possible is used)\n"
  1736. " -r, --rows [i] Numbers of rows to show (without this the max\n"
  1737. " amount possible is used)\n"
  1738. " -b, --border [i] Size of the border around the icons and prompt\n"
  1739. " (default: 1/10th of screen width)\n"
  1740. " This can also be set to 'auto' in order to\n"
  1741. " automatically calculate a border taking into\n"
  1742. " account the margin settings and the\n"
  1743. " configured columns and rows. You can also specify\n"
  1744. " border in terms of percentage of screen width by\n"
  1745. " appending a %% sign to the value\n"
  1746. " -B, --sideborder [i] Size of the border on the sides, if this is used\n"
  1747. " --border will be only top and bottom. Similarily\n"
  1748. " this can be set to 'auto' or a percentage but\n"
  1749. " then only side borders are calculated\n"
  1750. " --borderratio [i] The ratio of the border to apply above the\n"
  1751. " content. 0 is no top border, only bottom. 100 is\n"
  1752. " only top border, no bottom\n"
  1753. " --sideborderratio [i] The ratio of the side border to apply to the\n"
  1754. " left of the content. 0 is no left border, only\n"
  1755. " right. 100 is only left border, no right\n"
  1756. " -C, --center Center entries when there are fewer entries on a\n"
  1757. " row than the maximum\n"
  1758. " -P, --promptspacing [i] Distance between the prompt and the icons\n"
  1759. " (default: 48)\n"
  1760. " -s, --iconsize [i] Size of the icons (default: 48)\n"
  1761. " -a, --textafter Draw the title to the right of the icon instead\n"
  1762. " of below, this option automatically sets\n"
  1763. " --columns to 1 but this can be overridden.\n"
  1764. " -O, --textotherside Draw the text on the other side of the icon from\n"
  1765. " where it is normally drawn.\n"
  1766. " -u, --upsidedown Draw the prompt on the bottom and have icons\n"
  1767. " sort from bottom to top.\n"
  1768. " -X, --paddingswap Icon padding and text padding swaps order\n"
  1769. " around text.\n"
  1770. " -l, --leastmargin [i] Adds a margin to the calculation of\n"
  1771. " application sizes.\n"
  1772. " -V, --leastvmargin [i] Adds a vertical margin to the calculation of\n"
  1773. " application sizes.\n"
  1774. " -e, --hidemissing Hide entries with missing or broken icon images\n"
  1775. " --tc, --textcolor [color] Color to use for the text on the format rrggbbaa\n"
  1776. " (default: ffffffff)\n"
  1777. " --pc, --promptcolor [color] Color to use for the prompt text\n"
  1778. " (default: ffffffff)\n"
  1779. " --bc, --backgroundcolor [color] Color to use for the background\n"
  1780. " (default: 2e3440ff) NOTE: transparent background\n"
  1781. " color requires a compositor\n"
  1782. " --sc, --shadowcolor [color] Color to use for text shadows (default: 00000030)\n"
  1783. " --hc, --highlightcolor [color] Color to use for the highlight box\n"
  1784. " (default: ffffff32)\n\n");
  1785. // Check if we came from the error block above or if this was a call with --help
  1786. if(c == '?'){
  1787. exit(CONFIGERROR);
  1788. } else {
  1789. exit(OKAY);
  1790. }
  1791. break;
  1792. }
  1793. }
  1794. void init(int argc, char **argv)
  1795. {
  1796. // If no configuration file was explicitly given, try to read a default one
  1797. if (read_config == 0) {
  1798. FILE *config_source;
  1799. char * homeconf = NULL;
  1800. char * home = getenv("HOME");
  1801. if (home!=NULL)
  1802. {
  1803. homeconf = concat(home,"/.config/xlunch/xlunch.conf");
  1804. }
  1805. config_source = fopen(homeconf, "rb");
  1806. if(config_source == NULL) {
  1807. config_source = fopen("/etc/xlunch/default.conf", "rb");
  1808. }
  1809. free(homeconf);
  1810. if(config_source != NULL){
  1811. parse_config(config_source);
  1812. fclose(config_source);
  1813. }
  1814. }
  1815. // Handle options from the command line
  1816. int c, option_index;
  1817. while ((c = getopt_long(argc, argv, "vdr:nNg:L:b:B:s:i:p:f:mc:x:y:w:h:oatGHI:T:P:WF:SqROMuXeCl:V:U:A:", long_options, &option_index)) != -1) {
  1818. handle_option(c, optarg);
  1819. }
  1820. if (least_v_margin == -1) least_v_margin = least_margin;
  1821. if (icon_v_padding == -1) icon_v_padding = icon_padding;
  1822. /* connect to X */
  1823. disp = XOpenDisplay(NULL);
  1824. if (!disp)
  1825. {
  1826. fprintf(stderr,"Cannot connect to DISPLAY\n");
  1827. exit(WINERROR);
  1828. }
  1829. if (!XMatchVisualInfo(disp, DefaultScreen(disp), 32, TrueColor, &vinfo)) {
  1830. if (!XMatchVisualInfo(disp, DefaultScreen(disp), 24, TrueColor, &vinfo)) {
  1831. if (!XMatchVisualInfo(disp, DefaultScreen(disp), 16, DirectColor, &vinfo)) {
  1832. XMatchVisualInfo(disp, DefaultScreen(disp), 8, PseudoColor, &vinfo);
  1833. }
  1834. }
  1835. }
  1836. attr.colormap = XCreateColormap(disp, DefaultRootWindow(disp), vinfo.visual, AllocNone);
  1837. attr.border_pixel = 0;
  1838. attr.background_pixel = 0;
  1839. /* get default visual , colormap etc. you could ask imlib2 for what it */
  1840. /* thinks is the best, but this example is intended to be simple */
  1841. screen = DefaultScreen(disp);
  1842. /* get/set screen size */
  1843. if (uwidth==0) screen_width=DisplayWidth(disp,screen);
  1844. else screen_width=uwidth;
  1845. if (uheight==0) screen_height=DisplayHeight(disp,screen);
  1846. else screen_height=uheight;
  1847. // calculate relative positions if they are negative
  1848. if (uposx < 0) uposx = DisplayWidth(disp,screen) + uposx - uwidth;
  1849. if (uposy < 0) uposy = DisplayHeight(disp,screen) + uposy - uheight;
  1850. calculate_percentage(screen_height, &uborder);
  1851. calculate_percentage(screen_width, &uside_border);
  1852. }
  1853. void recheckHover(XEvent ev) {
  1854. node_t * current = entries;
  1855. int i = 1;
  1856. int j = 1;
  1857. int any_hovered = 0;
  1858. while (current != NULL)
  1859. {
  1860. if (mouse_moves>3 && mouse_over_cell(current, j, ev.xmotion.x, ev.xmotion.y)) {
  1861. set_hover(i, current, 1);
  1862. any_hovered = 1;
  1863. hoverset=MOUSE;
  1864. }
  1865. else {
  1866. set_hover(i, current,0);
  1867. set_clicked(current,0);
  1868. }
  1869. if(!current->hidden)
  1870. j++;
  1871. i++;
  1872. current = current->next;
  1873. }
  1874. if (any_hovered == 0) hovered_entry = 0;
  1875. button_t * button = buttons;
  1876. while (button != NULL) {
  1877. int x = (button->x < 0 ? screen_width + button->x + 1 - button->w : button->x);
  1878. int y = (button->y < 0 ? screen_height + button->y + 1 - button->h : button->y);
  1879. if (mouse_over_button(button, ev.xmotion.x, ev.xmotion.y)) {
  1880. if (button->hovered != 1) updates = imlib_update_append_rect(updates, x, y, button->w, button->h);
  1881. button->hovered = 1;
  1882. } else {
  1883. if (button->hovered != 0) updates = imlib_update_append_rect(updates, x, y, button->w, button->h);
  1884. button->hovered = 0;
  1885. button->clicked = 0;
  1886. }
  1887. button = button->next;
  1888. }
  1889. }
  1890. void handleButtonPress(XEvent ev) {
  1891. switch (ev.xbutton.button) {
  1892. case 3: // right click
  1893. if (!desktop_mode) {
  1894. cleanup();
  1895. exit(RIGHTCLICK);
  1896. }
  1897. break;
  1898. case 4: // scroll up
  1899. set_scroll_level(scrolled_past - 1);
  1900. recheckHover(ev);
  1901. break;
  1902. case 5: // scroll down
  1903. set_scroll_level(scrolled_past + 1);
  1904. recheckHover(ev);
  1905. break;
  1906. case 1:; // left click
  1907. node_t * current = entries;
  1908. int voidclicked = 1;
  1909. int index = 1;
  1910. mouse_moves += 4;
  1911. recheckHover(ev);
  1912. while (current != NULL)
  1913. {
  1914. if (mouse_over_cell(current, index, ev.xmotion.x, ev.xmotion.y)) {
  1915. set_clicked(current,1);
  1916. voidclicked = 0;
  1917. }
  1918. else set_clicked(current,0);
  1919. if(!current->hidden)
  1920. index++;
  1921. current = current->next;
  1922. }
  1923. button_t * button = buttons;
  1924. while (button != NULL) {
  1925. int x = (button->x < 0 ? screen_width + button->x + 1 - button->w : button->x);
  1926. int y = (button->y < 0 ? screen_height + button->y + 1 - button->h : button->y);
  1927. if (mouse_over_button(button, ev.xmotion.x, ev.xmotion.y)) {
  1928. if (button->clicked != 1) updates = imlib_update_append_rect(updates, x, y, button->w, button->h);
  1929. button->clicked = 1;
  1930. voidclicked = 0;
  1931. } else {
  1932. if (button->clicked != 0) updates = imlib_update_append_rect(updates, x, y, button->w, button->h);
  1933. button->clicked = 0;
  1934. }
  1935. button = button->next;
  1936. }
  1937. if (voidclicked && void_click_terminate) {
  1938. cleanup();
  1939. exit(VOIDCLICK);
  1940. }
  1941. break;
  1942. }
  1943. }
  1944. void handleButtonRelease(XEvent ev) {
  1945. node_t * current = entries;
  1946. int index = 1;
  1947. while (current != NULL)
  1948. {
  1949. if (mouse_over_cell(current, index, ev.xmotion.x, ev.xmotion.y)) if (current->clicked==1) run_command(current->cmd);
  1950. set_clicked(current, 0); // button release means all cells are not clicked
  1951. updates = imlib_update_append_rect(updates, current->x, current->y, icon_size, icon_size);
  1952. if(!current->hidden)
  1953. index++;
  1954. current = current->next;
  1955. }
  1956. button_t * button = buttons;
  1957. while (button != NULL)
  1958. {
  1959. if (mouse_over_button(button, ev.xmotion.x, ev.xmotion.y) && button->clicked == 1) run_command(button->cmd);
  1960. button->clicked = 0;
  1961. int x = (button->x < 0 ? screen_width + button->x + 1 - button->w : button->x);
  1962. int y = (button->y < 0 ? screen_height + button->y + 1 - button->h : button->y);
  1963. updates = imlib_update_append_rect(updates, x, y, button->w, button->h);
  1964. button = button->next;
  1965. }
  1966. }
  1967. void handleKeyPress(XEvent ev) {
  1968. // keyboard events
  1969. int count = 0;
  1970. KeySym keycode = 0;
  1971. Status status = 0;
  1972. char kbdbuf[20]= {0};
  1973. count = Xutf8LookupString(ic, (XKeyPressedEvent*)&ev, kbdbuf, 20, &keycode, &status);
  1974. if (keycode==XK_Escape && !desktop_mode)
  1975. {
  1976. cleanup();
  1977. exit(ESCAPE);
  1978. }
  1979. if (keycode==XK_Return || keycode==XK_KP_Enter)
  1980. {
  1981. // if we have an icon hovered, and the hover was caused by keyboard arrows, run the hovered icon
  1982. node_t * current = entries;
  1983. node_t * selected = NULL;
  1984. node_t * selected_one = NULL;
  1985. node_t * first = NULL;
  1986. int nb=0;
  1987. while (current != NULL)
  1988. {
  1989. if (!current->hidden)
  1990. {
  1991. nb++;
  1992. selected_one=current;
  1993. if (first == NULL) first = current;
  1994. if (current->hovered) selected = current;
  1995. }
  1996. current=current->next;
  1997. }
  1998. /* if only 1 app was filtered, consider it selected */
  1999. if (nb==1 && selected_one!=NULL) run_command(selected_one->cmd);
  2000. else if (hoverset==KEYBOARD && selected!=NULL) run_command(selected->cmd);
  2001. // else run the command entered by commandline, if the command prompt is used
  2002. else if (!no_prompt && !select_only) run_command(commandline);
  2003. // or if --selectonly is specified, run first program regardless of selected state
  2004. else if (select_only && first!=NULL) run_command(first->cmd);
  2005. }
  2006. if (keycode==XK_Tab || keycode==XK_Up || keycode==XK_Down || keycode==XK_Left || keycode==XK_Right
  2007. || keycode==XK_KP_Up || keycode==XK_KP_Down || keycode==XK_KP_Left || keycode==XK_KP_Right
  2008. || keycode==XK_Page_Down || keycode==XK_Page_Up || keycode==XK_Home || keycode==XK_End)
  2009. {
  2010. int i=0;
  2011. if (keycode==XK_KP_Left || keycode==XK_Left) i=-1;
  2012. if (keycode==XK_Up || keycode==XK_KP_Up) i=-columns;
  2013. if (keycode==XK_Down || keycode==XK_KP_Down) i=columns;
  2014. if (keycode==XK_Tab || keycode==XK_Right || keycode==XK_KP_Right) i=1;
  2015. if (keycode==XK_Page_Up) i=-columns*rows;
  2016. if (keycode==XK_Page_Down) i=columns*rows;
  2017. if (keycode==XK_End) i = entries_count;//(scroll ? scrolled_past*columns+n : n);
  2018. if (keycode==XK_Home) i = -entries_count;//(scroll ? scrolled_past*columns+1 : 1);
  2019. if (hovered_entry == 0) {
  2020. if (keycode != XK_End && keycode != XK_Page_Down) {
  2021. hovered_entry = 1-i;
  2022. } else {
  2023. hovered_entry = 1;
  2024. }
  2025. }
  2026. i = hovered_entry + i;
  2027. hover_entry(i);
  2028. return;
  2029. }
  2030. if (keycode==XK_Delete || keycode==XK_BackSpace)
  2031. pop_key();
  2032. else if (count>1 || (count==1 && kbdbuf[0]>=32)){ // ignore unprintable characterrs
  2033. if (!no_prompt) push_key(kbdbuf);
  2034. shortcut_t *shortcut = shortcuts;
  2035. while(shortcut != NULL) {
  2036. for(int i = 0; i < count; i++){
  2037. if (kbdbuf[i] != shortcut->key[i]) {
  2038. break;
  2039. } else if (i == count-1) {
  2040. run_command(shortcut->entry->cmd);
  2041. }
  2042. }
  2043. shortcut = shortcut->next;
  2044. }
  2045. }
  2046. joincmdline();
  2047. joincmdlinetext();
  2048. filter_entries();
  2049. arrange_positions();
  2050. // we used keyboard to type command. So unselect all icons.
  2051. node_t * current = entries;
  2052. int i = 1;
  2053. while (current != NULL)
  2054. {
  2055. set_hover(i, current,0);
  2056. set_clicked(current,0);
  2057. current = current->next;
  2058. i++;
  2059. }
  2060. updates = imlib_update_append_rect(updates, 0, 0, screen_width, screen_height);
  2061. }
  2062. void renderEntry(Imlib_Image buffer, char title[256], node_t * current, Cursor * c, int up_x, int up_y) {
  2063. int h = 0;
  2064. int w = 0;
  2065. if (current->hovered)
  2066. {
  2067. if (hoverset == MOUSE) *c = XCreateFontCursor(disp,XC_hand1);
  2068. if (highlight)
  2069. {
  2070. imlib_context_set_image(highlight);
  2071. w = imlib_image_get_width();
  2072. h = imlib_image_get_height();
  2073. imlib_context_set_image(buffer);
  2074. imlib_blend_image_onto_image(highlight, 1, 0, 0, w, h, current->x-up_x, current->y-up_y, cell_width, cell_height);
  2075. }
  2076. else
  2077. {
  2078. imlib_context_set_image(buffer);
  2079. imlib_context_set_color(highlight_color.r, highlight_color.g, highlight_color.b, highlight_color.a);
  2080. imlib_image_fill_rectangle(current->x-up_x, current->y-up_y, cell_width, cell_height);
  2081. }
  2082. }
  2083. if (strlen(current->icon) != 0) {
  2084. image=imlib_load_image(current->icon);
  2085. if (image)
  2086. {
  2087. imlib_context_set_image(image);
  2088. w = imlib_image_get_width();
  2089. h = imlib_image_get_height();
  2090. imlib_context_set_image(buffer);
  2091. imlib_image_set_has_alpha(0);
  2092. int d;
  2093. if (current->clicked) d=2;
  2094. else d=0;
  2095. int x = current->x - up_x +
  2096. (text_other_side && text_after ? cell_width - icon_padding - icon_size : icon_padding)+d;
  2097. int y = current->y - up_y +(text_other_side && !text_after ? cell_height - icon_v_padding - icon_size : icon_v_padding)+d;
  2098. imlib_blend_image_onto_image(image, 1, 0, 0, w, h, x, y, icon_size-d*2, icon_size-d*2);
  2099. imlib_context_set_image(image);
  2100. imlib_free_image();
  2101. }
  2102. }
  2103. /* draw text under icon */
  2104. Imlib_Font font = load_font();
  2105. if (font && !no_title)
  2106. {
  2107. imlib_context_set_image(buffer);
  2108. int text_w;
  2109. int text_h;
  2110. const size_t osz = strlen(current->title);
  2111. size_t sz = osz;
  2112. imlib_context_set_font(font);
  2113. do
  2114. {
  2115. strncpyutf8(title,current->title,sz);
  2116. if(sz != osz)
  2117. strcat(title,"..");
  2118. imlib_get_text_size(title, &text_w, &text_h);
  2119. sz--;
  2120. } while(text_w > cell_width-(text_after ? (icon_size != 0 ? icon_padding*2 : icon_padding) + icon_size + text_padding : 2*text_padding) && sz>0);
  2121. int d;
  2122. if (current->clicked==1) d=4;
  2123. else d=0;
  2124. if (text_after) {
  2125. draw_text_with_shadow(current->x - up_x + (text_other_side ? text_padding : (icon_size != 0 ? (padding_swap ? icon_padding*2 : icon_padding + text_padding) : icon_padding) + icon_size), current->y - up_y + cell_height/2 - font_height/2, title, text_color);
  2126. } else {
  2127. draw_text_with_shadow(current->x - up_x + cell_width/2 - text_w/2, current->y - up_y + (text_other_side ? text_padding : (padding_swap ? icon_v_padding*2 : icon_v_padding + text_padding) + icon_size), title, text_color);
  2128. }
  2129. /* free the font */
  2130. imlib_free_font();
  2131. }
  2132. }
  2133. /* the program... */
  2134. int main(int argc, char **argv){
  2135. /* events we get from X */
  2136. XEvent ev;
  2137. /* our virtual framebuffer image we draw into */
  2138. Imlib_Image buffer;
  2139. char title[255];
  2140. /* width and height values */
  2141. int w, h, x, y;
  2142. // set initial variables now
  2143. init(argc, argv);
  2144. if(program_name == NULL) {
  2145. program_name = getenv("RESOURCE_NAME");
  2146. if(program_name == NULL) {
  2147. program_name = argv[0];
  2148. }
  2149. }
  2150. joincmdlinetext();
  2151. // If an instance is already running, quit
  2152. if (!multiple_instances)
  2153. {
  2154. int oldmask = umask(0);
  2155. lock = open("/tmp/xlunch.lock", O_CREAT | O_RDWR, 0666);
  2156. umask(oldmask);
  2157. int rc = flock(lock, LOCK_EX | LOCK_NB);
  2158. if (rc) {
  2159. if (errno == EWOULDBLOCK) fprintf(stderr,"xlunch already running. You may want to consider --multiple\nIf this is an error, you may remove /tmp/xlunch.lock\n");
  2160. exit(LOCKERROR);
  2161. }
  2162. }
  2163. //win = XCreateSimpleWindow(disp, DefaultRootWindow(disp), uposx, uposy, screen_width, screen_height, 0, 0, 0);
  2164. win = XCreateWindow(disp, DefaultRootWindow(disp), uposx, uposy, screen_width, screen_height, 0, vinfo.depth, InputOutput, vinfo.visual, CWColormap | CWBorderPixel | CWBackPixel, &attr);
  2165. // absolute fullscreen mode by overide redirect
  2166. if (!windowed && desktop_mode)
  2167. {
  2168. unsigned long valuemask = CWOverrideRedirect;
  2169. XSetWindowAttributes attributes;
  2170. attributes.override_redirect=True;
  2171. XChangeWindowAttributes(disp,win,valuemask,&attributes);
  2172. }
  2173. /* add the ttf fonts dir to our font path */
  2174. char* homedir;
  2175. if((homedir = getenv("HOME")) != NULL) {
  2176. imlib_add_path_to_font_path(concat(homedir,"/.local/share/fonts"));
  2177. imlib_add_path_to_font_path(concat(homedir,"/.fonts"));
  2178. }
  2179. imlib_add_path_to_font_path("/usr/local/share/fonts");
  2180. imlib_add_path_to_font_path("/usr/share/fonts/truetype");
  2181. imlib_add_path_to_font_path("/usr/share/fonts/truetype/dejavu");
  2182. imlib_add_path_to_font_path("/usr/share/fonts/TTF");
  2183. /* set our cache to 2 Mb so it doesn't have to go hit the disk as long as */
  2184. /* the images we use use less than 2Mb of RAM (that is uncompressed) */
  2185. imlib_set_cache_size(2048 * screen_width);
  2186. /* set the font cache to 512Kb - again to avoid re-loading */
  2187. imlib_set_font_cache_size(512 * screen_width);
  2188. /* Preload fonts so that recalc can know their sizes */
  2189. load_font();
  2190. load_prompt_font();
  2191. recalc_cells();
  2192. /* set the maximum number of colors to allocate for 8bpp and less to 128 */
  2193. imlib_set_color_usage(128);
  2194. /* dither for depths < 24bpp */
  2195. imlib_context_set_dither(1);
  2196. /* set the display , visual, colormap and drawable we are using */
  2197. imlib_context_set_display(disp);
  2198. imlib_context_set_visual(vinfo.visual);
  2199. imlib_context_set_colormap(attr.colormap);
  2200. imlib_context_set_drawable(win);
  2201. update_background_images();
  2202. /* tell X what events we are interested in */
  2203. XSelectInput(disp, win, StructureNotifyMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask | ExposureMask | KeyPressMask | KeyReleaseMask | KeymapStateMask | FocusChangeMask );
  2204. GC gc = XCreateGC(disp, win, 0, 0);
  2205. /* set our name */
  2206. XClassHint* classHint;
  2207. /* set the titlebar name */
  2208. XStoreName(disp, win, "xlunch: Graphical app launcher");
  2209. /* set the name and class hints for the window manager to use */
  2210. classHint = XAllocClassHint();
  2211. if (classHint) {
  2212. classHint->res_name = basename(program_name);
  2213. classHint->res_class = (windowed ? "xlunch-windowed" : (desktop_mode ? "xlunch-desktop" : "xlunch-fullscreen"));
  2214. }
  2215. XSetClassHint(disp, win, classHint);
  2216. XFree(classHint);
  2217. /* show the window */
  2218. XMapRaised(disp, win);
  2219. /* Force window reposition, can make effect only when windowed is enabled, depending on WM */
  2220. if (force_reposition)
  2221. XMoveWindow(disp,win,uposx,uposy);
  2222. // prepare for keyboard UTF8 input
  2223. if (XSetLocaleModifiers("@im=none") == NULL) {
  2224. cleanup();
  2225. exit(LOCALEERROR);
  2226. }
  2227. im = XOpenIM(disp, NULL, NULL, NULL);
  2228. if (im == NULL) {
  2229. fputs("Could not open input method\n", stdout);
  2230. cleanup();
  2231. exit(INPUTMERROR);
  2232. }
  2233. ic = XCreateIC(im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, win, NULL);
  2234. if (ic == NULL) {
  2235. fprintf(stderr,"Could not open input context, without it we can't properly handle UTF8\n");
  2236. cleanup();
  2237. exit(INPUTCERROR);
  2238. }
  2239. XSetICFocus(ic);
  2240. // send to back or front, depending on settings
  2241. restack();
  2242. // parse entries file
  2243. input_source = determine_input_source();
  2244. if(input_source != NULL){
  2245. parse_entries();
  2246. }
  2247. // prepare message for window manager that we are requesting fullscreen
  2248. XClientMessageEvent msg = {
  2249. .type = ClientMessage,
  2250. .display = disp,
  2251. .window = win,
  2252. .message_type = XInternAtom(disp, "_NET_WM_STATE", True),
  2253. .format = 32,
  2254. .data = {
  2255. .l = {
  2256. 1 /* _NET_WM_STATE_ADD */,
  2257. XInternAtom(disp, "_NET_WM_STATE_FULLSCREEN", True),
  2258. None,
  2259. 0,
  2260. 1
  2261. }
  2262. }
  2263. };
  2264. // send the message
  2265. if (!windowed && !desktop_mode)
  2266. XSendEvent(disp, DefaultRootWindow(disp), False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&msg);
  2267. // Get the FD of the X11 display
  2268. x11_fd = ConnectionNumber(disp);
  2269. eventfds[0].fd = x11_fd;
  2270. eventfds[0].events = POLLIN || POLLPRI || POLLOUT;
  2271. if(input_source == stdin) {
  2272. eventfds[1].fd = 0; /* this is STDIN */
  2273. eventfds[1].events = POLLIN;
  2274. }
  2275. /* infinite event loop */
  2276. for (;;)
  2277. {
  2278. /* init our updates to empty */
  2279. updates = imlib_updates_init();
  2280. // Poll for events, while blocking until one becomes available
  2281. int poll_result;
  2282. if(!XPending(disp)){
  2283. poll_result = poll(eventfds, (input_source == stdin ? 2 : 1), -1);
  2284. } else {
  2285. poll_result = 1;
  2286. eventfds[0].revents = 1;
  2287. }
  2288. if(poll_result < 0){
  2289. // An error occured, abort
  2290. cleanup();
  2291. exit(POLLERROR);
  2292. } else {
  2293. if(input_source == stdin && eventfds[1].revents != 0){
  2294. int changed = parse_entries(input_source);
  2295. if(changed){
  2296. updates = imlib_update_append_rect(updates, 0, 0, screen_width, screen_height);
  2297. }
  2298. }
  2299. if(eventfds[0].revents != 0) {
  2300. /* while there are events form X - handle them */
  2301. while (XPending(disp))
  2302. {
  2303. XNextEvent(disp, &ev);
  2304. // allow through only UTF8 events
  2305. if (XFilterEvent(&ev, win)) continue;
  2306. switch (ev.type)
  2307. {
  2308. case Expose:
  2309. /* window rectangle was exposed - add it to the list of */
  2310. /* rectangles we need to re-render */
  2311. updates = imlib_update_append_rect(updates, ev.xexpose.x, ev.xexpose.y, ev.xexpose.width, ev.xexpose.height);
  2312. break;
  2313. case FocusIn:
  2314. restack();
  2315. break;
  2316. case FocusOut:
  2317. restack();
  2318. if (focus_lost_terminate) {
  2319. cleanup();
  2320. exit(FOCUSLOST);
  2321. }
  2322. break;
  2323. case ConfigureNotify:
  2324. if (screen_width!=ev.xconfigure.width || screen_height!=ev.xconfigure.height)
  2325. {
  2326. screen_width=ev.xconfigure.width;
  2327. screen_height=ev.xconfigure.height;
  2328. calculate_percentage(screen_height, &uborder);
  2329. calculate_percentage(screen_width, &uside_border);
  2330. if (!use_root_img) update_background_images();
  2331. recalc_cells();
  2332. arrange_positions();
  2333. updates = imlib_update_append_rect(updates, 0, 0, screen_width, screen_height);
  2334. }
  2335. break;
  2336. case ButtonPress:
  2337. handleButtonPress(ev);
  2338. break;
  2339. case ButtonRelease:
  2340. handleButtonRelease(ev);
  2341. break;
  2342. // refresh keyboard layout if changed
  2343. case KeymapNotify:
  2344. XRefreshKeyboardMapping(&ev.xmapping);
  2345. break;
  2346. case KeyPress:
  2347. handleKeyPress(ev);
  2348. break;
  2349. case KeyRelease:
  2350. break;
  2351. case LeaveNotify:
  2352. case EnterNotify:
  2353. case MotionNotify:
  2354. mouse_moves++;
  2355. recheckHover(ev);
  2356. break;
  2357. default:
  2358. /* any other events - do nothing */
  2359. break;
  2360. }
  2361. }
  2362. }
  2363. /* no more events for now ? ok - idle time so lets draw stuff */
  2364. /* take all the little rectangles to redraw and merge them into */
  2365. /* something sane for rendering */
  2366. updates = imlib_updates_merge_for_rendering(updates, screen_width, screen_height);
  2367. for (current_update = updates; current_update; current_update = imlib_updates_get_next(current_update))
  2368. {
  2369. int up_x, up_y, up_w, up_h;
  2370. /* find out where the first update is */
  2371. imlib_updates_get_coordinates(current_update, &up_x, &up_y, &up_w, &up_h);
  2372. /* create our buffer image for rendering this update */
  2373. buffer = imlib_create_image(up_w, up_h);
  2374. /* we can blend stuff now */
  2375. imlib_context_set_blend(1);
  2376. imlib_context_set_image(buffer);
  2377. imlib_image_set_has_alpha(1);
  2378. imlib_image_clear();
  2379. /* blend background image onto the buffer */
  2380. imlib_blend_image_onto_image(background, 1, 0, 0, screen_width, screen_height, - up_x, - up_y, screen_width, screen_height);
  2381. node_t * current = entries;
  2382. int drawn = 0;
  2383. int seen = 0;
  2384. Cursor c = XCreateFontCursor(disp,XC_top_left_arrow);
  2385. while (current != NULL)
  2386. {
  2387. if (!current->hidden)
  2388. {
  2389. if (seen++ < scrolled_past * columns) {
  2390. current = current->next;
  2391. continue;
  2392. }
  2393. renderEntry(buffer, title, current, &c, up_x, up_y);
  2394. if (++drawn == columns*rows)
  2395. break;
  2396. }
  2397. current = current->next;
  2398. }
  2399. button_t *button = buttons;
  2400. while (button != NULL) {
  2401. if(button->hovered) c = XCreateFontCursor(disp,XC_hand1);
  2402. image = imlib_load_image(button->hovered ? (button->icon_highlight[0] == '\0' ? button->icon_normal : button->icon_highlight) : button->icon_normal);
  2403. if (image)
  2404. {
  2405. imlib_context_set_image(buffer);
  2406. imlib_image_set_has_alpha(0);
  2407. int d;
  2408. if (button->clicked) d=2;
  2409. else d=0;
  2410. int x = (button->x < 0 ? screen_width + button->x + 1 - button->w : button->x);
  2411. int y = (button->y < 0 ? screen_height + button->y + 1 - button->h : button->y);
  2412. imlib_image_copy_alpha_to_image(image,x - up_x + d, y - up_y + d);
  2413. imlib_blend_image_onto_image(image, 1, 0, 0, button->w, button->h, x - up_x + d, y - up_y + d, button->w-d*2, button->h-d*2);
  2414. imlib_context_set_image(image);
  2415. imlib_free_image();
  2416. }
  2417. button = button->next;
  2418. }
  2419. XDefineCursor(disp,win,c);
  2420. /* set the buffer image as our current image */
  2421. imlib_context_set_image(buffer);
  2422. /* draw prompt */
  2423. if (!no_prompt) {
  2424. Imlib_Font font = load_prompt_font();
  2425. if (font)
  2426. {
  2427. imlib_context_set_font(font);
  2428. if(upside_down) {
  2429. draw_text_with_shadow(prompt_x - up_x, (screen_height - prompt_font_height) - (prompt_y - up_y), commandlinetext, prompt_color);
  2430. } else {
  2431. draw_text_with_shadow(prompt_x - up_x, prompt_y - up_y, commandlinetext, prompt_color);
  2432. }
  2433. /* free the font */
  2434. imlib_free_font();
  2435. }
  2436. }
  2437. /* draw scrollbar, currently not draggable, only indication of scroll */
  2438. if (!noscroll)
  2439. {
  2440. int scrollbar_width=15; // width of entire scrollbar
  2441. int scrollbar_height=screen_height-2*border; // height of entire scrollbar
  2442. int scrollbar_screen_margin=50; // distance from screen edge
  2443. int pages = (entries_count - 1) / rows / columns + 1; // total pages to scroll, round up, min 1
  2444. // show scrollbar only if there are more pages
  2445. if (pages > 1)
  2446. {
  2447. int scrollbar_draggable_height = scrollbar_height * rows * columns / (entries_count+columns - ((entries_count+columns-1)%columns)); // height of scrollbar page (draggable), round to whole rows
  2448. float p = (entries_count - 1) / columns + 1 - rows; // current scrolled percentage
  2449. int scrollbar_draggable_shift = p ? (scrollbar_height - scrollbar_draggable_height) * scrolled_past / p : 0;
  2450. imlib_context_set_blend(1);
  2451. // draw scrollbar background on full height
  2452. imlib_context_set_color(255,255,255,60);
  2453. imlib_image_fill_rectangle(screen_width - scrollbar_screen_margin, border, scrollbar_width, scrollbar_height);
  2454. // draw current scroll position
  2455. imlib_context_set_color(255,255,255,112);
  2456. imlib_image_fill_rectangle(screen_width - scrollbar_screen_margin, border + scrollbar_draggable_shift, scrollbar_width, scrollbar_draggable_height);
  2457. imlib_context_set_blend(0);
  2458. }
  2459. }
  2460. /* don't blend the image onto the drawable - slower */
  2461. imlib_context_set_blend(0);
  2462. /* render the image at 0, 0 */
  2463. imlib_render_image_on_drawable(up_x, up_y);
  2464. /* don't need that temporary buffer image anymore */
  2465. imlib_free_image();
  2466. }
  2467. }
  2468. /* if we had updates - free them */
  2469. if (updates)
  2470. imlib_updates_free(updates);
  2471. /* loop again waiting for events */
  2472. }
  2473. return 0;
  2474. }