panel.c 35 KB

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