panel.c 34 KB

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