panel.c 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2013-2018 K. Lange
  5. *
  6. * Yutani Panel
  7. *
  8. * Provides an applications menu, a window list, status widgets,
  9. * and a clock, manages the user session, and provides alt-tab
  10. * window switching and alt-f2 app runner.
  11. *
  12. */
  13. #include <stdlib.h>
  14. #include <assert.h>
  15. #include <limits.h>
  16. #include <math.h>
  17. #include <time.h>
  18. #include <fcntl.h>
  19. #include <unistd.h>
  20. #include <signal.h>
  21. #include <sys/time.h>
  22. #include <sys/utsname.h>
  23. #include <sys/wait.h>
  24. #include <sys/stat.h>
  25. #include <sys/ioctl.h>
  26. #include <sys/fswait.h>
  27. #include <toaru/yutani.h>
  28. #include <toaru/graphics.h>
  29. #include <toaru/hashmap.h>
  30. #include <toaru/spinlock.h>
  31. #include <toaru/sdf.h>
  32. #include <toaru/icon_cache.h>
  33. #include <toaru/menu.h>
  34. #include <kernel/mod/sound.h>
  35. #define PANEL_HEIGHT 28
  36. #define FONT_SIZE 14
  37. #define TIME_LEFT 108
  38. #define DATE_WIDTH 70
  39. #define GRADIENT_HEIGHT 24
  40. #define APP_OFFSET 140
  41. #define TEXT_Y_OFFSET 2
  42. #define ICON_PADDING 2
  43. #define MAX_TEXT_WIDTH 180
  44. #define MIN_TEXT_WIDTH 50
  45. #define HILIGHT_COLOR rgb(142,216,255)
  46. #define FOCUS_COLOR rgb(255,255,255)
  47. #define TEXT_COLOR rgb(230,230,230)
  48. #define ICON_COLOR rgb(230,230,230)
  49. #define GRADIENT_AT(y) premultiply(rgba(72, 167, 255, ((24-(y))*160)/24))
  50. #define ALTTAB_WIDTH 250
  51. #define ALTTAB_HEIGHT 100
  52. #define ALTTAB_BACKGROUND premultiply(rgba(0,0,0,150))
  53. #define ALTTAB_OFFSET 10
  54. #define ALTF2_WIDTH 400
  55. #define ALTF2_HEIGHT 200
  56. #define MAX_WINDOW_COUNT 100
  57. #define TOTAL_CELL_WIDTH (title_width)
  58. #define LEFT_BOUND (width - TIME_LEFT - DATE_WIDTH - ICON_PADDING - widgets_width)
  59. #define WIDGET_WIDTH 24
  60. #define WIDGET_RIGHT (width - TIME_LEFT - DATE_WIDTH)
  61. #define WIDGET_POSITION(i) (WIDGET_RIGHT - WIDGET_WIDTH * (i+1))
  62. static yutani_t * yctx;
  63. static gfx_context_t * ctx = NULL;
  64. static yutani_window_t * panel = NULL;
  65. static gfx_context_t * actx = NULL;
  66. static yutani_window_t * alttab = NULL;
  67. static gfx_context_t * a2ctx = NULL;
  68. static yutani_window_t * alt_f2 = NULL;
  69. static list_t * window_list = NULL;
  70. static volatile int lock = 0;
  71. static volatile int drawlock = 0;
  72. static size_t bg_size;
  73. static char * bg_blob;
  74. static int width;
  75. static int height;
  76. static int widgets_width = 0;
  77. static int widgets_volume_enabled = 0;
  78. static int widgets_network_enabled = 0;
  79. static int network_status = 0;
  80. static sprite_t * sprite_panel;
  81. static sprite_t * sprite_logout;
  82. static sprite_t * sprite_volume_mute;
  83. static sprite_t * sprite_volume_low;
  84. static sprite_t * sprite_volume_med;
  85. static sprite_t * sprite_volume_high;
  86. static sprite_t * sprite_net_active;
  87. static sprite_t * sprite_net_disabled;
  88. struct MenuList * appmenu;
  89. struct MenuList * window_menu;
  90. struct MenuList * logout_menu;
  91. struct MenuList * netstat;
  92. static yutani_wid_t _window_menu_wid = 0;
  93. static int _close_enough(struct yutani_msg_window_mouse_event * me) {
  94. if (me->command == YUTANI_MOUSE_EVENT_RAISE && sqrt(pow(me->new_x - me->old_x, 2) + pow(me->new_y - me->old_y, 2)) < 10) {
  95. return 1;
  96. }
  97. return 0;
  98. }
  99. static int center_x(int x) {
  100. return (width - x) / 2;
  101. }
  102. static int center_y(int y) {
  103. return (height - y) / 2;
  104. }
  105. static int center_x_a(int x) {
  106. return (ALTTAB_WIDTH - x) / 2;
  107. }
  108. static int center_x_a2(int x) {
  109. return (ALTF2_WIDTH - x) / 2;
  110. }
  111. static void redraw(void);
  112. static volatile int _continue = 1;
  113. struct window_ad {
  114. yutani_wid_t wid;
  115. uint32_t flags;
  116. char * name;
  117. char * icon;
  118. char * strings;
  119. int left;
  120. };
  121. typedef struct {
  122. char * icon;
  123. char * appname;
  124. char * title;
  125. } application_t;
  126. /* Windows, indexed by list order */
  127. static struct window_ad * ads_by_l[MAX_WINDOW_COUNT+1] = {NULL};
  128. /* Windows, indexed by z-order */
  129. static struct window_ad * ads_by_z[MAX_WINDOW_COUNT+1] = {NULL};
  130. static int focused_app = -1;
  131. static int active_window = -1;
  132. static int was_tabbing = 0;
  133. static int new_focused = -1;
  134. static int title_width = 0;
  135. static void toggle_hide_panel(void) {
  136. static int panel_hidden = 0;
  137. if (panel_hidden) {
  138. /* Unhide the panel */
  139. for (int i = PANEL_HEIGHT-1; i >= 0; i--) {
  140. yutani_window_move(yctx, panel, 0, -i);
  141. usleep(10000);
  142. }
  143. panel_hidden = 0;
  144. } else {
  145. /* Hide the panel */
  146. for (int i = 1; i <= PANEL_HEIGHT-1; i++) {
  147. yutani_window_move(yctx, panel, 0, -i);
  148. usleep(10000);
  149. }
  150. panel_hidden = 1;
  151. }
  152. }
  153. /* Handle SIGINT by telling other threads (clock) to shut down */
  154. static void sig_int(int sig) {
  155. printf("Received shutdown signal in panel!\n");
  156. _continue = 0;
  157. }
  158. static void launch_application(char * app) {
  159. if (!fork()) {
  160. printf("Starting %s\n", app);
  161. char * args[] = {"/bin/sh", "-c", app, NULL};
  162. execvp(args[0], args);
  163. exit(1);
  164. }
  165. }
  166. /* Update the hover-focus window */
  167. static void set_focused(int i) {
  168. if (focused_app != i) {
  169. focused_app = i;
  170. redraw();
  171. }
  172. }
  173. static void _window_menu_start_move(struct MenuEntry * self) {
  174. if (!_window_menu_wid)
  175. return;
  176. yutani_focus_window(yctx, _window_menu_wid);
  177. yutani_window_drag_start_wid(yctx, _window_menu_wid);
  178. }
  179. static void _window_menu_start_maximize(struct MenuEntry * self) {
  180. if (!_window_menu_wid)
  181. return;
  182. yutani_special_request_wid(yctx, _window_menu_wid, YUTANI_SPECIAL_REQUEST_MAXIMIZE);
  183. yutani_focus_window(yctx, _window_menu_wid);
  184. }
  185. static void _window_menu_close(struct MenuEntry * self) {
  186. if (!_window_menu_wid)
  187. return;
  188. yutani_focus_window(yctx, _window_menu_wid);
  189. yutani_special_request_wid(yctx, _window_menu_wid, YUTANI_SPECIAL_REQUEST_PLEASE_CLOSE);
  190. }
  191. static void window_show_menu(yutani_wid_t wid, int y, int x) {
  192. if (window_menu->window) return;
  193. _window_menu_wid = wid;
  194. menu_show(window_menu, yctx);
  195. yutani_window_move(yctx, window_menu->window, y, x);
  196. }
  197. #define VOLUME_DEVICE_ID 0
  198. #define VOLUME_KNOB_ID 0
  199. static uint32_t volume_level = 0;
  200. static int mixer = -1;
  201. static void update_volume_level(void) {
  202. if (mixer == -1) {
  203. mixer = open("/dev/mixer", O_RDONLY);
  204. }
  205. snd_knob_value_t value = {0};
  206. value.device = VOLUME_DEVICE_ID; /* TODO configure this somewhere */
  207. value.id = VOLUME_KNOB_ID; /* TODO this too */
  208. ioctl(mixer, SND_MIXER_READ_KNOB, &value);
  209. volume_level = value.val;
  210. }
  211. static void volume_raise(void) {
  212. if (volume_level > 0xE0000000) volume_level = 0xF0000000;
  213. else volume_level += 0x10000000;
  214. snd_knob_value_t value = {0};
  215. value.device = VOLUME_DEVICE_ID; /* TODO configure this somewhere */
  216. value.id = VOLUME_KNOB_ID; /* TODO this too */
  217. value.val = volume_level;
  218. ioctl(mixer, SND_MIXER_WRITE_KNOB, &value);
  219. redraw();
  220. }
  221. static void volume_lower(void) {
  222. if (volume_level < 0x20000000) volume_level = 0x0;
  223. else volume_level -= 0x10000000;
  224. snd_knob_value_t value = {0};
  225. value.device = VOLUME_DEVICE_ID; /* TODO configure this somewhere */
  226. value.id = VOLUME_KNOB_ID; /* TODO this too */
  227. value.val = volume_level;
  228. ioctl(mixer, SND_MIXER_WRITE_KNOB, &value);
  229. redraw();
  230. }
  231. static int netstat_left = 0;
  232. static struct MenuEntry_Normal * netstat_ip_entry;
  233. static char * netstat_ip = NULL;
  234. static struct MenuEntry_Normal * netstat_device_entry;
  235. static char * netstat_device = NULL;
  236. static struct MenuEntry_Normal * netstat_gateway_entry;
  237. static char * netstat_gateway = NULL;
  238. static struct MenuEntry_Normal * netstat_dns_entry;
  239. static char * netstat_dns = NULL;
  240. static struct MenuEntry_Normal * netstat_mac_entry;
  241. static char * netstat_mac = NULL;
  242. static void update_network_status(void) {
  243. FILE * net = fopen("/proc/netif","r");
  244. if (!net) return;
  245. char line[256];
  246. do {
  247. memset(line, 0, 256);
  248. fgets(line, 256, net);
  249. if (!*line) break;
  250. if (strstr(line,"no network") != NULL) {
  251. network_status = 0;
  252. break;
  253. } else if (strstr(line,"ip:") != NULL) {
  254. network_status = 1;
  255. if (netstat_ip) {
  256. free(netstat_ip);
  257. }
  258. char tmp[512];
  259. sprintf(tmp, "IP: %s", &line[strlen("ip: ")]);
  260. char * lf = strstr(tmp,"\n");
  261. if (lf) *lf = '\0';
  262. netstat_ip = strdup(tmp);
  263. } else if (strstr(line,"device:") != NULL) {
  264. network_status = 1;
  265. if (netstat_device) {
  266. free(netstat_device);
  267. }
  268. char tmp[512];
  269. sprintf(tmp, "Device: %s", &line[strlen("device: ")]);
  270. char * lf = strstr(tmp,"\n");
  271. if (lf) *lf = '\0';
  272. netstat_device = strdup(tmp);
  273. } else if (strstr(line,"gateway:") != NULL) {
  274. network_status = 1;
  275. if (netstat_gateway) {
  276. free(netstat_gateway);
  277. }
  278. char tmp[512];
  279. sprintf(tmp, "Gateway: %s", &line[strlen("gateway: ")]);
  280. char * lf = strstr(tmp,"\n");
  281. if (lf) *lf = '\0';
  282. netstat_gateway = strdup(tmp);
  283. } else if (strstr(line,"dns:") != NULL) {
  284. network_status = 1;
  285. if (netstat_dns) {
  286. free(netstat_dns);
  287. }
  288. char tmp[512];
  289. sprintf(tmp, "Primary DNS: %s", &line[strlen("dns: ")]);
  290. char * lf = strstr(tmp,"\n");
  291. if (lf) *lf = '\0';
  292. netstat_dns = strdup(tmp);
  293. } else if (strstr(line,"mac:") != NULL) {
  294. network_status = 1;
  295. if (netstat_mac) {
  296. free(netstat_mac);
  297. }
  298. char tmp[512];
  299. sprintf(tmp, "MAC: %s", &line[strlen("mac: ")]);
  300. char * lf = strstr(tmp,"\n");
  301. if (lf) *lf = '\0';
  302. netstat_mac = strdup(tmp);
  303. }
  304. } while (1);
  305. fclose(net);
  306. }
  307. static void show_logout_menu(void) {
  308. if (!logout_menu->window) {
  309. menu_show(logout_menu, yctx);
  310. if (logout_menu->window) {
  311. yutani_window_move(yctx, logout_menu->window, width - logout_menu->window->width, PANEL_HEIGHT);
  312. }
  313. }
  314. }
  315. static void show_app_menu(void) {
  316. if (!appmenu->window) {
  317. menu_show(appmenu, yctx);
  318. if (appmenu->window) {
  319. yutani_window_move(yctx, appmenu->window, 0, PANEL_HEIGHT);
  320. }
  321. }
  322. }
  323. static void show_network_status(void) {
  324. if (!netstat) {
  325. netstat = menu_create();
  326. menu_insert(netstat, menu_create_normal(NULL, NULL, "Network Status", NULL));
  327. menu_insert(netstat, menu_create_separator());
  328. netstat_ip_entry = (struct MenuEntry_Normal *)menu_create_normal(NULL, NULL, "", NULL);
  329. menu_insert(netstat, netstat_ip_entry);
  330. netstat_dns_entry = (struct MenuEntry_Normal *)menu_create_normal(NULL, NULL, "", NULL);
  331. menu_insert(netstat, netstat_dns_entry);
  332. netstat_gateway_entry = (struct MenuEntry_Normal *)menu_create_normal(NULL, NULL, "", NULL);
  333. menu_insert(netstat, netstat_gateway_entry);
  334. netstat_mac_entry = (struct MenuEntry_Normal *)menu_create_normal(NULL, NULL, "", NULL);
  335. menu_insert(netstat, netstat_mac_entry);
  336. netstat_device_entry = (struct MenuEntry_Normal *)menu_create_normal(NULL, NULL, "", NULL);
  337. menu_insert(netstat, netstat_device_entry);
  338. }
  339. if (network_status) {
  340. menu_update_title(netstat_ip_entry, netstat_ip);
  341. menu_update_title(netstat_device_entry, netstat_device ? netstat_device : "(?)");
  342. menu_update_title(netstat_dns_entry, netstat_dns ? netstat_dns : "(?)");
  343. menu_update_title(netstat_gateway_entry, netstat_gateway ? netstat_gateway : "(?)");
  344. menu_update_title(netstat_mac_entry, netstat_mac ? netstat_mac : "(?)");
  345. } else {
  346. menu_update_title(netstat_ip_entry, "No network.");
  347. menu_update_title(netstat_device_entry, "");
  348. menu_update_title(netstat_dns_entry, "");
  349. menu_update_title(netstat_gateway_entry, "");
  350. menu_update_title(netstat_mac_entry, "");
  351. }
  352. if (!netstat->window) {
  353. menu_show(netstat, yctx);
  354. if (netstat->window) {
  355. if (netstat_left + netstat->window->width > (unsigned int)width) {
  356. yutani_window_move(yctx, netstat->window, width - netstat->window->width, PANEL_HEIGHT);
  357. } else {
  358. yutani_window_move(yctx, netstat->window, netstat_left, PANEL_HEIGHT);
  359. }
  360. }
  361. }
  362. }
  363. /* Callback for mouse events */
  364. static void panel_check_click(struct yutani_msg_window_mouse_event * evt) {
  365. if (evt->wid == panel->wid) {
  366. if (evt->command == YUTANI_MOUSE_EVENT_CLICK || _close_enough(evt)) {
  367. /* Up-down click */
  368. if (evt->new_x >= width - 24 ) {
  369. show_logout_menu();
  370. } else if (evt->new_x < APP_OFFSET) {
  371. show_app_menu();
  372. } else if (evt->new_x >= APP_OFFSET && evt->new_x < LEFT_BOUND) {
  373. for (int i = 0; i < MAX_WINDOW_COUNT; ++i) {
  374. if (ads_by_l[i] == NULL) break;
  375. if (evt->new_x >= ads_by_l[i]->left && evt->new_x < ads_by_l[i]->left + TOTAL_CELL_WIDTH) {
  376. yutani_focus_window(yctx, ads_by_l[i]->wid);
  377. break;
  378. }
  379. }
  380. }
  381. int widget = 0;
  382. if (widgets_network_enabled) {
  383. if (evt->new_x > WIDGET_POSITION(widget) && evt->new_x < WIDGET_POSITION(widget-1)) {
  384. netstat_left = WIDGET_POSITION(widget);
  385. show_network_status();
  386. }
  387. widget++;
  388. }
  389. if (widgets_volume_enabled) {
  390. if (evt->new_x > WIDGET_POSITION(widget) && evt->new_x < WIDGET_POSITION(widget-1)) {
  391. /* TODO: Show the volume manager */
  392. }
  393. widget++;
  394. }
  395. } else if (evt->buttons & YUTANI_MOUSE_BUTTON_RIGHT) {
  396. if (evt->new_x >= APP_OFFSET && evt->new_x < LEFT_BOUND) {
  397. for (int i = 0; i < MAX_WINDOW_COUNT; ++i) {
  398. if (ads_by_l[i] == NULL) break;
  399. if (evt->new_x >= ads_by_l[i]->left && evt->new_x < ads_by_l[i]->left + TOTAL_CELL_WIDTH) {
  400. window_show_menu(ads_by_l[i]->wid, evt->new_x, PANEL_HEIGHT);
  401. }
  402. }
  403. }
  404. } else if (evt->command == YUTANI_MOUSE_EVENT_MOVE || evt->command == YUTANI_MOUSE_EVENT_ENTER) {
  405. /* Movement, or mouse entered window */
  406. if (evt->new_y < PANEL_HEIGHT) {
  407. for (int i = 0; i < MAX_WINDOW_COUNT; ++i) {
  408. if (ads_by_l[i] == NULL) {
  409. set_focused(-1);
  410. break;
  411. }
  412. if (evt->new_x >= ads_by_l[i]->left && evt->new_x < ads_by_l[i]->left + TOTAL_CELL_WIDTH) {
  413. set_focused(i);
  414. break;
  415. }
  416. }
  417. } else {
  418. set_focused(-1);
  419. }
  420. int scroll_direction = 0;
  421. if (evt->buttons & YUTANI_MOUSE_SCROLL_UP) scroll_direction = -1;
  422. else if (evt->buttons & YUTANI_MOUSE_SCROLL_DOWN) scroll_direction = 1;
  423. if (scroll_direction) {
  424. int widget = 0;
  425. if (widgets_network_enabled) {
  426. if (evt->new_x > WIDGET_POSITION(widget) && evt->new_x < WIDGET_POSITION(widget-1)) {
  427. /* Ignore */
  428. }
  429. widget++;
  430. }
  431. if (widgets_volume_enabled) {
  432. if (evt->new_x > WIDGET_POSITION(widget) && evt->new_x < WIDGET_POSITION(widget-1)) {
  433. if (scroll_direction == 1) {
  434. volume_lower();
  435. } else if (scroll_direction == -1) {
  436. volume_raise();
  437. }
  438. }
  439. widget++;
  440. }
  441. if (evt->new_x >= APP_OFFSET && evt->new_x < LEFT_BOUND) {
  442. if (scroll_direction != 0) {
  443. struct window_ad * last = window_list->tail ? window_list->tail->value : NULL;
  444. int focus_next = 0;
  445. foreach(node, window_list) {
  446. struct window_ad * ad = node->value;
  447. if (focus_next) {
  448. yutani_focus_window(yctx, ad->wid);
  449. return;
  450. }
  451. if (ad->flags & 1) {
  452. if (scroll_direction == -1) {
  453. yutani_focus_window(yctx, last->wid);
  454. return;
  455. }
  456. if (scroll_direction == 1) {
  457. focus_next = 1;
  458. }
  459. }
  460. last = ad;
  461. }
  462. if (focus_next && window_list->head) {
  463. struct window_ad * ad = window_list->head->value;
  464. yutani_focus_window(yctx, ad->wid);
  465. return;
  466. }
  467. }
  468. }
  469. }
  470. } else if (evt->command == YUTANI_MOUSE_EVENT_LEAVE) {
  471. /* Mouse left panel window */
  472. set_focused(-1);
  473. }
  474. }
  475. }
  476. static char altf2_buffer[1024] = {0};
  477. static unsigned int altf2_collected = 0;
  478. #if 0
  479. static list_t * altf2_apps = NULL;
  480. struct altf2_app {
  481. char * name;
  482. sprite_t * icon;
  483. };
  484. static sprite_t * find_icon(char * name) {
  485. struct {
  486. char * name;
  487. char * icon;
  488. } special[] = {
  489. {"about", "star"},
  490. {"help-browser", "help"},
  491. {"terminal", "utilities-terminal"},
  492. {NULL,NULL},
  493. };
  494. int i = 0;
  495. while (special[i].name) {
  496. if (!strcmp(special[i].name, name)) {
  497. return icon_get_48(special[i].icon);
  498. }
  499. i++;
  500. }
  501. return icon_get_48(name);
  502. }
  503. #endif
  504. static void close_altf2(void) {
  505. free(a2ctx->backbuffer);
  506. free(a2ctx);
  507. altf2_buffer[0] = 0;
  508. altf2_collected = 0;
  509. yutani_close(yctx, alt_f2);
  510. alt_f2 = NULL;
  511. }
  512. static void redraw_altf2(void) {
  513. #if 0
  514. if (!altf2_apps) {
  515. /* initialize */
  516. }
  517. #endif
  518. draw_fill(a2ctx, 0);
  519. draw_rounded_rectangle(a2ctx,0,0, ALTF2_WIDTH, ALTF2_HEIGHT, 10, ALTTAB_BACKGROUND);
  520. int t = draw_sdf_string_width(altf2_buffer, 22, SDF_FONT_THIN);
  521. draw_sdf_string(a2ctx, center_x_a2(t), 60, altf2_buffer, 22, rgb(255,255,255), SDF_FONT_THIN);
  522. flip(a2ctx);
  523. yutani_flip(yctx, alt_f2);
  524. }
  525. static void redraw_alttab(void) {
  526. /* Draw the background, right now just a dark semi-transparent box */
  527. draw_fill(actx, 0);
  528. draw_rounded_rectangle(actx,0,0, ALTTAB_WIDTH, ALTTAB_HEIGHT, 10, ALTTAB_BACKGROUND);
  529. if (ads_by_z[new_focused]) {
  530. struct window_ad * ad = ads_by_z[new_focused];
  531. sprite_t * icon = icon_get_48(ad->icon);
  532. /* Draw it, scaled if necessary */
  533. if (icon->width == 48) {
  534. draw_sprite(actx, icon, center_x_a(48), ALTTAB_OFFSET);
  535. } else {
  536. draw_sprite_scaled(actx, icon, center_x_a(48), ALTTAB_OFFSET, 48, 48);
  537. }
  538. int t = draw_sdf_string_width(ad->name, 18, SDF_FONT_THIN);
  539. draw_sdf_string(actx, center_x_a(t), 12+ALTTAB_OFFSET+40, ad->name, 18, rgb(255,255,255), SDF_FONT_THIN);
  540. }
  541. flip(actx);
  542. yutani_flip(yctx, alttab);
  543. }
  544. static void launch_application_menu(struct MenuEntry * self) {
  545. struct MenuEntry_Normal * _self = (void *)self;
  546. if (!strcmp((char *)_self->action,"log-out")) {
  547. yutani_session_end(yctx);
  548. _continue = 0;
  549. } else {
  550. launch_application((char *)_self->action);
  551. }
  552. }
  553. static void handle_key_event(struct yutani_msg_key_event * ke) {
  554. if (alt_f2 && ke->wid == alt_f2->wid) {
  555. if (ke->event.action == KEY_ACTION_DOWN) {
  556. if (ke->event.keycode == KEY_ESCAPE) {
  557. close_altf2();
  558. return;
  559. }
  560. if (ke->event.key == '\b') {
  561. if (altf2_collected) {
  562. altf2_buffer[altf2_collected-1] = '\0';
  563. altf2_collected--;
  564. redraw_altf2();
  565. }
  566. return;
  567. }
  568. if (ke->event.key == '\n') {
  569. /* execute */
  570. launch_application(altf2_buffer);
  571. close_altf2();
  572. return;
  573. }
  574. if (!ke->event.key) {
  575. return;
  576. }
  577. /* Try to add it */
  578. if (altf2_collected < sizeof(altf2_buffer) - 1) {
  579. altf2_buffer[altf2_collected] = ke->event.key;
  580. altf2_collected++;
  581. altf2_buffer[altf2_collected] = 0;
  582. redraw_altf2();
  583. }
  584. }
  585. }
  586. if ((ke->event.modifiers & KEY_MOD_LEFT_CTRL) &&
  587. (ke->event.modifiers & KEY_MOD_LEFT_ALT) &&
  588. (ke->event.keycode == 't') &&
  589. (ke->event.action == KEY_ACTION_DOWN)) {
  590. launch_application("exec terminal");
  591. return;
  592. }
  593. if ((ke->event.modifiers & KEY_MOD_LEFT_CTRL) &&
  594. (ke->event.keycode == KEY_F11) &&
  595. (ke->event.action == KEY_ACTION_DOWN)) {
  596. fprintf(stderr, "[panel] Toggling visibility.\n");
  597. toggle_hide_panel();
  598. return;
  599. }
  600. if ((ke->event.modifiers & KEY_MOD_LEFT_ALT) &&
  601. (ke->event.keycode == KEY_F1) &&
  602. (ke->event.action == KEY_ACTION_DOWN)) {
  603. /* show menu */
  604. show_app_menu();
  605. }
  606. if ((ke->event.modifiers & KEY_MOD_LEFT_ALT) &&
  607. (ke->event.keycode == KEY_F2) &&
  608. (ke->event.action == KEY_ACTION_DOWN)) {
  609. /* show menu */
  610. if (!alt_f2) {
  611. alt_f2 = yutani_window_create(yctx, ALTF2_WIDTH, ALTF2_HEIGHT);
  612. yutani_window_move(yctx, alt_f2, center_x(ALTF2_WIDTH), center_y(ALTF2_HEIGHT));
  613. a2ctx = init_graphics_yutani_double_buffer(alt_f2);
  614. redraw_altf2();
  615. }
  616. }
  617. if ((was_tabbing) && (ke->event.keycode == 0 || ke->event.keycode == KEY_LEFT_ALT) &&
  618. (ke->event.modifiers == 0) && (ke->event.action == KEY_ACTION_UP)) {
  619. fprintf(stderr, "[panel] Stopping focus new_focused = %d\n", new_focused);
  620. struct window_ad * ad = ads_by_z[new_focused];
  621. if (!ad) return;
  622. yutani_focus_window(yctx, ad->wid);
  623. was_tabbing = 0;
  624. new_focused = -1;
  625. free(actx->backbuffer);
  626. free(actx);
  627. yutani_close(yctx, alttab);
  628. return;
  629. }
  630. if ((ke->event.modifiers & KEY_MOD_LEFT_ALT) &&
  631. (ke->event.keycode == '\t') &&
  632. (ke->event.action == KEY_ACTION_DOWN)) {
  633. int direction = (ke->event.modifiers & KEY_MOD_LEFT_SHIFT) ? 1 : -1;
  634. if (window_list->length < 1) return;
  635. if (was_tabbing) {
  636. new_focused = new_focused + direction;
  637. } else {
  638. new_focused = active_window + direction;
  639. /* Create tab window */
  640. alttab = yutani_window_create(yctx, ALTTAB_WIDTH, ALTTAB_HEIGHT);
  641. /* Center window */
  642. yutani_window_move(yctx, alttab, center_x(ALTTAB_WIDTH), center_y(ALTTAB_HEIGHT));
  643. /* Initialize graphics context against the window */
  644. actx = init_graphics_yutani_double_buffer(alttab);
  645. }
  646. if (new_focused < 0) {
  647. new_focused = 0;
  648. for (int i = 0; i < MAX_WINDOW_COUNT; i++) {
  649. if (ads_by_z[i+1] == NULL) {
  650. new_focused = i;
  651. break;
  652. }
  653. }
  654. } else if (ads_by_z[new_focused] == NULL) {
  655. new_focused = 0;
  656. }
  657. was_tabbing = 1;
  658. redraw_alttab();
  659. }
  660. }
  661. static void redraw(void) {
  662. spin_lock(&drawlock);
  663. struct timeval now;
  664. struct tm * timeinfo;
  665. char buffer[80];
  666. uint32_t txt_color = TEXT_COLOR;
  667. int t = 0;
  668. /* Redraw the background */
  669. memcpy(ctx->backbuffer, bg_blob, bg_size);
  670. /* Get the current time for the clock */
  671. gettimeofday(&now, NULL);
  672. timeinfo = localtime((time_t *)&now.tv_sec);
  673. /* Hours : Minutes : Seconds */
  674. strftime(buffer, 80, "%H:%M:%S", timeinfo);
  675. draw_sdf_string(ctx, width - TIME_LEFT, 3, buffer, 20, txt_color, SDF_FONT_THIN);
  676. /* Day-of-week */
  677. strftime(buffer, 80, "%A", timeinfo);
  678. t = draw_sdf_string_width(buffer, 12, SDF_FONT_THIN);
  679. t = (DATE_WIDTH - t) / 2;
  680. draw_sdf_string(ctx, width - TIME_LEFT - DATE_WIDTH + t, 2, buffer, 12, txt_color, SDF_FONT_THIN);
  681. /* Month Day */
  682. strftime(buffer, 80, "%h %e", timeinfo);
  683. t = draw_sdf_string_width(buffer, 12, SDF_FONT_BOLD);
  684. t = (DATE_WIDTH - t) / 2;
  685. draw_sdf_string(ctx, width - TIME_LEFT - DATE_WIDTH + t, 12, buffer, 12, txt_color, SDF_FONT_BOLD);
  686. /* Applications menu */
  687. draw_sdf_string(ctx, 8, 3, "Applications", 20, appmenu->window ? HILIGHT_COLOR : txt_color, SDF_FONT_THIN);
  688. /* Draw each widget */
  689. /* - Volume */
  690. int widget = 0;
  691. if (widgets_network_enabled) {
  692. uint32_t color = (netstat && netstat->window) ? HILIGHT_COLOR : ICON_COLOR;
  693. if (network_status == 1) {
  694. draw_sprite_alpha_paint(ctx, sprite_net_active, WIDGET_POSITION(widget), 0, 1.0, color);
  695. } else {
  696. draw_sprite_alpha_paint(ctx, sprite_net_disabled, WIDGET_POSITION(widget), 0, 1.0, color);
  697. }
  698. widget++;
  699. }
  700. if (widgets_volume_enabled) {
  701. if (volume_level < 10) {
  702. draw_sprite_alpha_paint(ctx, sprite_volume_mute, WIDGET_POSITION(widget), 0, 1.0, ICON_COLOR);
  703. } else if (volume_level < 0x547ae147) {
  704. draw_sprite_alpha_paint(ctx, sprite_volume_low, WIDGET_POSITION(widget), 0, 1.0, ICON_COLOR);
  705. } else if (volume_level < 0xa8f5c28e) {
  706. draw_sprite_alpha_paint(ctx, sprite_volume_med, WIDGET_POSITION(widget), 0, 1.0, ICON_COLOR);
  707. } else {
  708. draw_sprite_alpha_paint(ctx, sprite_volume_high, WIDGET_POSITION(widget), 0, 1.0, ICON_COLOR);
  709. }
  710. widget++;
  711. }
  712. /* Now draw the window list */
  713. int i = 0, j = 0;
  714. spin_lock(&lock);
  715. if (window_list) {
  716. foreach(node, window_list) {
  717. struct window_ad * ad = node->value;
  718. char * s = "";
  719. char tmp_title[50];
  720. int w = 0;
  721. if (APP_OFFSET + i + w > LEFT_BOUND) {
  722. break;
  723. }
  724. if (title_width > MIN_TEXT_WIDTH) {
  725. memset(tmp_title, 0x0, 50);
  726. int t_l = strlen(ad->name);
  727. if (t_l > 45) {
  728. t_l = 45;
  729. }
  730. for (int i = 0; i < t_l; ++i) {
  731. tmp_title[i] = ad->name[i];
  732. if (!ad->name[i]) break;
  733. }
  734. while (draw_sdf_string_width(tmp_title, 16, SDF_FONT_THIN) > title_width - ICON_PADDING) {
  735. t_l--;
  736. tmp_title[t_l] = '.';
  737. tmp_title[t_l+1] = '.';
  738. tmp_title[t_l+2] = '.';
  739. tmp_title[t_l+3] = '\0';
  740. }
  741. w += title_width;
  742. s = tmp_title;
  743. }
  744. /* Hilight the focused window */
  745. if (ad->flags & 1) {
  746. /* This is the focused window */
  747. for (int y = 0; y < GRADIENT_HEIGHT; ++y) {
  748. for (int x = APP_OFFSET + i; x < APP_OFFSET + i + w; ++x) {
  749. GFX(ctx, x, y) = alpha_blend_rgba(GFX(ctx, x, y), GRADIENT_AT(y));
  750. }
  751. }
  752. }
  753. /* Get the icon for this window */
  754. sprite_t * icon = icon_get_48(ad->icon);
  755. {
  756. sprite_t * _tmp_s = create_sprite(48, PANEL_HEIGHT-2, ALPHA_EMBEDDED);
  757. gfx_context_t * _tmp = init_graphics_sprite(_tmp_s);
  758. draw_fill(_tmp, rgba(0,0,0,0));
  759. /* Draw it, scaled if necessary */
  760. if (icon->width == 48) {
  761. draw_sprite(_tmp, icon, 0, 0);
  762. } else {
  763. draw_sprite_scaled(_tmp, icon, 0, 0, 48, 48);
  764. }
  765. free(_tmp);
  766. draw_sprite_alpha(ctx, _tmp_s, APP_OFFSET + i + w - 48 - 2, 0, 0.7);
  767. sprite_free(_tmp_s);
  768. }
  769. {
  770. sprite_t * _tmp_s = create_sprite(w, PANEL_HEIGHT, ALPHA_EMBEDDED);
  771. gfx_context_t * _tmp = init_graphics_sprite(_tmp_s);
  772. draw_fill(_tmp, rgba(0,0,0,0));
  773. draw_sdf_string(_tmp, 0, 0, s, 16, rgb(0,0,0), SDF_FONT_THIN);
  774. blur_context_box(_tmp, 4);
  775. free(_tmp);
  776. draw_sprite(ctx, _tmp_s, APP_OFFSET + i + 2, TEXT_Y_OFFSET + 2);
  777. sprite_free(_tmp_s);
  778. }
  779. if (title_width > MIN_TEXT_WIDTH) {
  780. /* Then draw the window title, with appropriate color */
  781. if (j == focused_app) {
  782. /* Current hilighted - title should be a light blue */
  783. draw_sdf_string(ctx, APP_OFFSET + i + 2, TEXT_Y_OFFSET + 2, s, 16, HILIGHT_COLOR, SDF_FONT_THIN);
  784. } else {
  785. if (ad->flags & 1) {
  786. /* Top window should be white */
  787. draw_sdf_string(ctx, APP_OFFSET + i + 2, TEXT_Y_OFFSET + 2, s, 16, FOCUS_COLOR, SDF_FONT_THIN);
  788. } else {
  789. /* Otherwise, off white */
  790. draw_sdf_string(ctx, APP_OFFSET + i + 2, TEXT_Y_OFFSET + 2, s, 16, txt_color, SDF_FONT_THIN);
  791. }
  792. }
  793. }
  794. /* XXX This keeps track of how far left each window list item is
  795. * so we can map clicks up in the mouse callback. */
  796. if (j < MAX_WINDOW_COUNT) {
  797. if (ads_by_l[j]) {
  798. ads_by_l[j]->left = APP_OFFSET + i;
  799. }
  800. }
  801. j++;
  802. i += w;
  803. }
  804. }
  805. spin_unlock(&lock);
  806. /* Draw the logout button; XXX This should probably have some sort of focus hilight */
  807. draw_sprite_alpha_paint(ctx, sprite_logout, width - 23, 1, 1.0, (logout_menu->window ? HILIGHT_COLOR : ICON_COLOR)); /* Logout button */
  808. /* Flip */
  809. flip(ctx);
  810. yutani_flip(yctx, panel);
  811. spin_unlock(&drawlock);
  812. }
  813. static void update_window_list(void) {
  814. yutani_query_windows(yctx);
  815. list_t * new_window_list = list_create();
  816. int i = 0;
  817. while (1) {
  818. /* We wait for a series of WINDOW_ADVERTISE messsages */
  819. yutani_msg_t * m = yutani_wait_for(yctx, YUTANI_MSG_WINDOW_ADVERTISE);
  820. struct yutani_msg_window_advertise * wa = (void*)m->data;
  821. if (wa->size == 0) {
  822. /* A sentinal at the end will have a size of 0 */
  823. free(m);
  824. break;
  825. }
  826. /* Store each window advertisement */
  827. struct window_ad * ad = malloc(sizeof(struct window_ad));
  828. char * s = malloc(wa->size);
  829. memcpy(s, wa->strings, wa->size);
  830. ad->name = &s[wa->offsets[0]];
  831. ad->icon = &s[wa->offsets[1]];
  832. ad->strings = s;
  833. ad->flags = wa->flags;
  834. ad->wid = wa->wid;
  835. ads_by_z[i] = ad;
  836. i++;
  837. ads_by_z[i] = NULL;
  838. node_t * next = NULL;
  839. /* And insert it, ordered by wid, into the window list */
  840. foreach(node, new_window_list) {
  841. struct window_ad * n = node->value;
  842. if (n->wid > ad->wid) {
  843. next = node;
  844. break;
  845. }
  846. }
  847. if (next) {
  848. list_insert_before(new_window_list, next, ad);
  849. } else {
  850. list_insert(new_window_list, ad);
  851. }
  852. free(m);
  853. }
  854. active_window = i-1;
  855. i = 0;
  856. /*
  857. * Update each of the wid entries in our array so we can map
  858. * clicks to window focus events for each window
  859. */
  860. foreach(node, new_window_list) {
  861. struct window_ad * ad = node->value;
  862. if (i < MAX_WINDOW_COUNT) {
  863. ads_by_l[i] = ad;
  864. ads_by_l[i+1] = NULL;
  865. }
  866. i++;
  867. }
  868. /* Then free up the old list and replace it with the new list */
  869. spin_lock(&lock);
  870. if (new_window_list->length) {
  871. int tmp = LEFT_BOUND;
  872. tmp -= APP_OFFSET;
  873. if (tmp < 0) {
  874. title_width = 0;
  875. } else {
  876. title_width = tmp / new_window_list->length;
  877. if (title_width > MAX_TEXT_WIDTH) {
  878. title_width = MAX_TEXT_WIDTH;
  879. }
  880. if (title_width < MIN_TEXT_WIDTH) {
  881. title_width = 0;
  882. }
  883. }
  884. } else {
  885. title_width = 0;
  886. }
  887. if (window_list) {
  888. foreach(node, window_list) {
  889. struct window_ad * ad = (void*)node->value;
  890. free(ad->strings);
  891. free(ad);
  892. }
  893. list_free(window_list);
  894. free(window_list);
  895. }
  896. window_list = new_window_list;
  897. spin_unlock(&lock);
  898. /* And redraw the panel */
  899. redraw();
  900. }
  901. static void resize_finish(int xwidth, int xheight) {
  902. yutani_window_resize_accept(yctx, panel, xwidth, xheight);
  903. reinit_graphics_yutani(ctx, panel);
  904. yutani_window_resize_done(yctx, panel);
  905. width = xwidth;
  906. /* Draw the background */
  907. draw_fill(ctx, rgba(0,0,0,0));
  908. for (int i = 0; i < xwidth; i += sprite_panel->width) {
  909. draw_sprite(ctx, sprite_panel, i, 0);
  910. }
  911. /* Copy the prerendered background so we can redraw it quickly */
  912. bg_size = panel->width * panel->height * sizeof(uint32_t);
  913. bg_blob = realloc(bg_blob, bg_size);
  914. memcpy(bg_blob, ctx->backbuffer, bg_size);
  915. update_window_list();
  916. redraw();
  917. }
  918. static void bind_keys(void) {
  919. /* Cltr-Alt-T = launch terminal */
  920. yutani_key_bind(yctx, 't', KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT, YUTANI_BIND_STEAL);
  921. /* Alt+Tab = app switcher*/
  922. yutani_key_bind(yctx, '\t', KEY_MOD_LEFT_ALT, YUTANI_BIND_STEAL);
  923. yutani_key_bind(yctx, '\t', KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT, YUTANI_BIND_STEAL);
  924. /* Ctrl-F11 = toggle panel visibility */
  925. yutani_key_bind(yctx, KEY_F11, KEY_MOD_LEFT_CTRL, YUTANI_BIND_STEAL);
  926. /* Alt+F1 = show menu */
  927. yutani_key_bind(yctx, KEY_F1, KEY_MOD_LEFT_ALT, YUTANI_BIND_STEAL);
  928. /* Alt+F2 = show app runner */
  929. yutani_key_bind(yctx, KEY_F2, KEY_MOD_LEFT_ALT, YUTANI_BIND_STEAL);
  930. /* This lets us receive all just-modifier key releases */
  931. yutani_key_bind(yctx, KEY_LEFT_ALT, 0, YUTANI_BIND_PASSTHROUGH);
  932. }
  933. static void sig_usr2(int sig) {
  934. yutani_set_stack(yctx, panel, YUTANI_ZORDER_TOP);
  935. yutani_flip(yctx, panel);
  936. bind_keys();
  937. }
  938. int main (int argc, char ** argv) {
  939. if (argc < 2 || strcmp(argv[1],"--really")) {
  940. fprintf(stderr,
  941. "%s: Desktop environment panel / dock\n"
  942. "\n"
  943. " Renders the application menu, window list, widgets,\n"
  944. " alt-tab window switcher, clock, etc.\n"
  945. " You probably don't want to run this directly - it is\n"
  946. " started automatically by the session manager.\n", argv[0]);
  947. return 1;
  948. }
  949. /* Connect to window server */
  950. yctx = yutani_init();
  951. /* For convenience, store the display size */
  952. width = yctx->display_width;
  953. height = yctx->display_height;
  954. /* Create the panel window */
  955. panel = yutani_window_create_flags(yctx, width, PANEL_HEIGHT, YUTANI_WINDOW_FLAG_NO_STEAL_FOCUS);
  956. /* And move it to the top layer */
  957. yutani_set_stack(yctx, panel, YUTANI_ZORDER_TOP);
  958. /* Initialize graphics context against the window */
  959. ctx = init_graphics_yutani_double_buffer(panel);
  960. /* Clear it out (the compositor should initialize it cleared anyway */
  961. draw_fill(ctx, rgba(0,0,0,0));
  962. flip(ctx);
  963. yutani_flip(yctx, panel);
  964. /* Load textures for the background and logout button */
  965. sprite_panel = malloc(sizeof(sprite_t));
  966. sprite_logout = malloc(sizeof(sprite_t));
  967. load_sprite(sprite_panel, "/usr/share/panel.bmp");
  968. sprite_panel->alpha = ALPHA_EMBEDDED;
  969. load_sprite(sprite_logout, "/usr/share/icons/panel-shutdown.bmp");
  970. sprite_logout->alpha = ALPHA_FORCE_SLOW_EMBEDDED;
  971. struct stat stat_tmp;
  972. if (!stat("/dev/dsp",&stat_tmp)) {
  973. widgets_volume_enabled = 1;
  974. widgets_width += WIDGET_WIDTH;
  975. sprite_volume_mute = malloc(sizeof(sprite_t));
  976. sprite_volume_low = malloc(sizeof(sprite_t));
  977. sprite_volume_med = malloc(sizeof(sprite_t));
  978. sprite_volume_high = malloc(sizeof(sprite_t));
  979. load_sprite(sprite_volume_mute, "/usr/share/icons/24/volume-mute.bmp");
  980. sprite_volume_mute->alpha = ALPHA_FORCE_SLOW_EMBEDDED;
  981. load_sprite(sprite_volume_low, "/usr/share/icons/24/volume-low.bmp");
  982. sprite_volume_low->alpha = ALPHA_FORCE_SLOW_EMBEDDED;
  983. load_sprite(sprite_volume_med, "/usr/share/icons/24/volume-medium.bmp");
  984. sprite_volume_med->alpha = ALPHA_FORCE_SLOW_EMBEDDED;
  985. load_sprite(sprite_volume_high, "/usr/share/icons/24/volume-full.bmp");
  986. sprite_volume_high->alpha = ALPHA_FORCE_SLOW_EMBEDDED;
  987. /* XXX store current volume */
  988. }
  989. {
  990. widgets_network_enabled = 1;
  991. widgets_width += WIDGET_WIDTH;
  992. sprite_net_active = malloc(sizeof(sprite_t));
  993. load_sprite(sprite_net_active, "/usr/share/icons/24/net-active.bmp");
  994. sprite_net_active->alpha = ALPHA_FORCE_SLOW_EMBEDDED;
  995. sprite_net_disabled = malloc(sizeof(sprite_t));
  996. load_sprite(sprite_net_disabled, "/usr/share/icons/24/net-disconnected.bmp");
  997. sprite_net_disabled->alpha = ALPHA_FORCE_SLOW_EMBEDDED;
  998. }
  999. /* Draw the background */
  1000. for (int i = 0; i < width; i += sprite_panel->width) {
  1001. draw_sprite(ctx, sprite_panel, i, 0);
  1002. }
  1003. /* Copy the prerendered background so we can redraw it quickly */
  1004. bg_size = panel->width * panel->height * sizeof(uint32_t);
  1005. bg_blob = malloc(bg_size);
  1006. memcpy(bg_blob, ctx->backbuffer, bg_size);
  1007. /* Catch SIGINT */
  1008. signal(SIGINT, sig_int);
  1009. signal(SIGUSR2, sig_usr2);
  1010. appmenu = menu_set_get_root(menu_set_from_description("/usr/share/demo.menu", launch_application_menu));
  1011. window_menu = menu_create();
  1012. menu_insert(window_menu, menu_create_normal(NULL, NULL, "Maximize", _window_menu_start_maximize));
  1013. menu_insert(window_menu, menu_create_normal(NULL, NULL, "Move", _window_menu_start_move));
  1014. menu_insert(window_menu, menu_create_separator());
  1015. menu_insert(window_menu, menu_create_normal(NULL, NULL, "Close", _window_menu_close));
  1016. logout_menu = menu_create();
  1017. menu_insert(logout_menu, menu_create_normal("exit", "log-out", "Log Out", launch_application_menu));
  1018. /* Subscribe to window updates */
  1019. yutani_subscribe_windows(yctx);
  1020. /* Ask compositor for window list */
  1021. update_window_list();
  1022. /* Key bindings */
  1023. bind_keys();
  1024. time_t last_tick = 0;
  1025. int fds[1] = {fileno(yctx->sock)};
  1026. while (_continue) {
  1027. int index = fswait2(1,fds,200);
  1028. if (index == 0) {
  1029. /* Respond to Yutani events */
  1030. yutani_msg_t * m = yutani_poll(yctx);
  1031. while (m) {
  1032. menu_process_event(yctx, m);
  1033. switch (m->type) {
  1034. /* New window information is available */
  1035. case YUTANI_MSG_NOTIFY:
  1036. update_window_list();
  1037. break;
  1038. /* Mouse movement / click */
  1039. case YUTANI_MSG_WINDOW_MOUSE_EVENT:
  1040. panel_check_click((struct yutani_msg_window_mouse_event *)m->data);
  1041. break;
  1042. case YUTANI_MSG_KEY_EVENT:
  1043. handle_key_event((struct yutani_msg_key_event *)m->data);
  1044. break;
  1045. case YUTANI_MSG_WELCOME:
  1046. {
  1047. struct yutani_msg_welcome * mw = (void*)m->data;
  1048. width = mw->display_width;
  1049. height = mw->display_height;
  1050. yutani_window_resize(yctx, panel, mw->display_width, PANEL_HEIGHT);
  1051. }
  1052. break;
  1053. case YUTANI_MSG_RESIZE_OFFER:
  1054. {
  1055. struct yutani_msg_window_resize * wr = (void*)m->data;
  1056. resize_finish(wr->width, wr->height);
  1057. }
  1058. break;
  1059. default:
  1060. break;
  1061. }
  1062. free(m);
  1063. m = yutani_poll_async(yctx);
  1064. }
  1065. } else {
  1066. struct timeval now;
  1067. gettimeofday(&now, NULL);
  1068. if (now.tv_sec != last_tick) {
  1069. last_tick = now.tv_sec;
  1070. waitpid(-1, NULL, WNOHANG);
  1071. update_volume_level();
  1072. update_network_status();
  1073. redraw();
  1074. }
  1075. }
  1076. }
  1077. /* Close the panel window */
  1078. yutani_close(yctx, panel);
  1079. /* Stop notifying us of window changes */
  1080. yutani_unsubscribe_windows(yctx);
  1081. return 0;
  1082. }