glogin-provider.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  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-2015 K. Lange
  5. */
  6. /*
  7. * glogin
  8. *
  9. * Graphical Login screen
  10. */
  11. #include <stdlib.h>
  12. #include <assert.h>
  13. #include <unistd.h>
  14. #include <math.h>
  15. #include <string.h>
  16. #include <time.h>
  17. #include <sys/utsname.h>
  18. #include <sys/wait.h>
  19. #include <sys/time.h>
  20. #include <toaru/graphics.h>
  21. #include <toaru/kbd.h>
  22. #include <toaru/yutani.h>
  23. #include <toaru/auth.h>
  24. #include <toaru/confreader.h>
  25. #include <toaru/sdf.h>
  26. #include <toaru/jpeg.h>
  27. #include <toaru/trace.h>
  28. #define TRACE_APP_NAME "glogin-provider"
  29. static sprite_t logo;
  30. static gfx_context_t * ctx;
  31. static uint16_t win_width;
  32. static uint16_t win_height;
  33. static int uid = 0;
  34. #define USERNAME_BOX 1
  35. #define PASSWORD_BOX 2
  36. static int LOGO_FINAL_OFFSET = 100;
  37. static int BOX_WIDTH = 272;
  38. static int BOX_HEIGHT = 104;
  39. static int BOX_ROUNDNESS = 8;
  40. static int CENTER_BOX_X=1;
  41. static int CENTER_BOX_Y=1;
  42. static int BOX_LEFT=-1;
  43. static int BOX_RIGHT=-1;
  44. static int BOX_TOP=-1;
  45. static int BOX_BOTTOM=-1;
  46. static int BOX_COLOR_R=0;
  47. static int BOX_COLOR_G=0;
  48. static int BOX_COLOR_B=0;
  49. static int BOX_COLOR_A=127;
  50. static char * WALLPAPER = "/usr/share/wallpaper.jpg";
  51. static char * LOGO = "/usr/share/logo_login.bmp";
  52. #define TEXTBOX_INTERIOR_LEFT 4
  53. #define EXTRA_TEXT_OFFSET 15
  54. int center_x(int x) {
  55. return (win_width - x) / 2;
  56. }
  57. int center_y(int y) {
  58. return (win_height - y) / 2;
  59. }
  60. #define INPUT_SIZE 1024
  61. int buffer_put(char * input_buffer, char c) {
  62. int input_collected = strlen(input_buffer);
  63. if (c == 8) {
  64. /* Backspace */
  65. if (input_collected > 0) {
  66. input_collected--;
  67. input_buffer[input_collected] = '\0';
  68. }
  69. return 0;
  70. }
  71. if (c < 10 || (c > 10 && c < 32) || c > 126) {
  72. return 0;
  73. }
  74. input_buffer[input_collected] = c;
  75. input_collected++;
  76. input_buffer[input_collected] = '\0';
  77. if (input_collected == INPUT_SIZE - 1) {
  78. return 1;
  79. }
  80. return 0;
  81. }
  82. struct text_box {
  83. int x;
  84. int y;
  85. unsigned int width;
  86. unsigned int height;
  87. uint32_t text_color;
  88. struct login_container * parent;
  89. int is_focused:1;
  90. int is_password:1;
  91. unsigned int cursor;
  92. char * buffer;
  93. char * placeholder;
  94. };
  95. struct login_container {
  96. int x;
  97. int y;
  98. unsigned int width;
  99. unsigned int height;
  100. struct text_box * username_box;
  101. struct text_box * password_box;
  102. int show_error:1;
  103. };
  104. void draw_text_box(gfx_context_t * ctx, struct text_box * tb) {
  105. int x = tb->parent->x + tb->x;
  106. int y = tb->parent->y + tb->y;
  107. int text_offset = 15;
  108. if (tb->is_focused) {
  109. draw_rounded_rectangle(ctx, 1 + x, 1 + y, tb->width - 2, tb->height - 2, 4, rgb(8,193,236));
  110. draw_rounded_rectangle(ctx, 2 + x, 2 + y, tb->width - 4, tb->height - 4, 4, rgb(244,244,244));
  111. } else {
  112. draw_rounded_rectangle(ctx, 1 + x, 1 + y, tb->width - 2, tb->height - 2, 4, rgb(158,169,177));
  113. }
  114. /* Line width 2? */
  115. char * text = tb->buffer;
  116. char password_circles[512];
  117. uint32_t color = tb->text_color;
  118. if (strlen(tb->buffer) == 0 && !tb->is_focused) {
  119. text = tb->placeholder;
  120. color = rgba(0,0,0,127);
  121. } else if (tb->is_password) {
  122. strcpy(password_circles, "");
  123. for (unsigned int i = 0; i < strlen(tb->buffer); ++i) {
  124. strcat(password_circles, "\007");
  125. }
  126. text = password_circles;
  127. }
  128. draw_sdf_string(ctx, x + TEXTBOX_INTERIOR_LEFT, y + text_offset - 12, text, 15, color, SDF_FONT_THIN);
  129. if (tb->is_focused) {
  130. int width = draw_sdf_string_width(text, 15, SDF_FONT_THIN) + 2;
  131. draw_line(ctx, x + TEXTBOX_INTERIOR_LEFT + width, x + TEXTBOX_INTERIOR_LEFT + width, y + 2, y + text_offset + 1, tb->text_color);
  132. }
  133. }
  134. void draw_login_container(gfx_context_t * ctx, struct login_container * lc) {
  135. /* Draw rounded rectangle */
  136. draw_rounded_rectangle(ctx, lc->x, lc->y, lc->width, lc->height, BOX_ROUNDNESS, rgba(BOX_COLOR_R,BOX_COLOR_G,BOX_COLOR_B,BOX_COLOR_A));
  137. /* Draw labels */
  138. if (lc->show_error) {
  139. char * error_message = "Incorrect username or password.";
  140. //set_font_size(11);
  141. //draw_string(ctx, lc->x + (lc->width - draw_string_width(error_message)) / 2, lc->y + 6 + EXTRA_TEXT_OFFSET, rgb(240, 20, 20), error_message);
  142. draw_sdf_string(ctx, lc->x + (lc->width - draw_sdf_string_width(error_message, 14, SDF_FONT_THIN)) / 2, lc->y + 6 + EXTRA_TEXT_OFFSET - 14, error_message, 14, rgb(240,20,20), SDF_FONT_THIN);
  143. }
  144. draw_text_box(ctx, lc->username_box);
  145. draw_text_box(ctx, lc->password_box);
  146. }
  147. /**
  148. * Get hostname information updated with the current time.
  149. *
  150. * @param hostname
  151. */
  152. static void get_updated_hostname_with_time_info(char hostname[]) {
  153. // get hostname
  154. char _hostname[256];
  155. gethostname(_hostname, 255);
  156. // get current time
  157. struct tm * timeinfo;
  158. struct timeval now;
  159. gettimeofday(&now, NULL); //time(NULL);
  160. timeinfo = localtime((time_t *)&now.tv_sec);
  161. // format the hostname info
  162. char _date[256];
  163. strftime(_date, 256, "%a %B %d %Y", timeinfo);
  164. sprintf(hostname, "%s // %s", _hostname, _date);
  165. }
  166. static void draw_sdf_string_shadow(gfx_context_t * ctx, char * string, int font_size, int left, int top, uint32_t text_color, uint32_t shadow_color, int blur, int font) {
  167. int w = draw_sdf_string_width(string, font_size, font);
  168. sprite_t * _tmp_s = create_sprite(w + blur * 2, font_size + blur * 2, ALPHA_EMBEDDED);
  169. gfx_context_t * _tmp = init_graphics_sprite(_tmp_s);
  170. draw_fill(_tmp, rgba(0,0,0,0));
  171. draw_sdf_string(_tmp, blur, blur, string, font_size, shadow_color, font);
  172. blur_context_box(_tmp, blur);
  173. free(_tmp);
  174. draw_sprite(ctx, _tmp_s, left - blur, top - blur);
  175. sprite_free(_tmp_s);
  176. draw_sdf_string(ctx, left, top, string, 14, text_color, font);
  177. }
  178. int main (int argc, char ** argv) {
  179. if (getuid() != 0) {
  180. return 1;
  181. }
  182. fprintf(stdout, "Hello\n");
  183. yutani_t * y = yutani_init();
  184. if (!y) {
  185. fprintf(stderr, "[glogin] Connection to server failed.\n");
  186. return 1;
  187. }
  188. /* Load config */
  189. {
  190. confreader_t * conf = confreader_load("/etc/glogin.conf");
  191. if (conf) {
  192. LOGO_FINAL_OFFSET = confreader_intd(conf, "style", "logo_padding", LOGO_FINAL_OFFSET);
  193. BOX_WIDTH = confreader_intd(conf, "style", "box_width", BOX_WIDTH);
  194. BOX_HEIGHT = confreader_intd(conf, "style", "box_height", BOX_HEIGHT);
  195. BOX_ROUNDNESS = confreader_intd(conf, "style", "box_roundness", BOX_ROUNDNESS);
  196. CENTER_BOX_X = confreader_intd(conf, "style", "center_box_x", CENTER_BOX_X);
  197. CENTER_BOX_Y = confreader_intd(conf, "style", "center_box_y", CENTER_BOX_Y);
  198. BOX_LEFT = confreader_intd(conf, "style", "box_left", BOX_LEFT);
  199. BOX_RIGHT = confreader_intd(conf, "style", "box_right", BOX_RIGHT);
  200. BOX_TOP = confreader_intd(conf, "style", "box_top", BOX_TOP);
  201. BOX_BOTTOM = confreader_intd(conf, "style", "box_bottom", BOX_BOTTOM);
  202. BOX_COLOR_R = confreader_intd(conf, "style", "box_color_r", BOX_COLOR_R);
  203. BOX_COLOR_G = confreader_intd(conf, "style", "box_color_g", BOX_COLOR_G);
  204. BOX_COLOR_B = confreader_intd(conf, "style", "box_color_b", BOX_COLOR_B);
  205. BOX_COLOR_A = confreader_intd(conf, "style", "box_color_a", BOX_COLOR_A);
  206. WALLPAPER = confreader_getd(conf, "image", "wallpaper", WALLPAPER);
  207. LOGO = confreader_getd(conf, "image", "logo", LOGO);
  208. confreader_free(conf);
  209. }
  210. TRACE("Loading complete");
  211. }
  212. TRACE("Loading logo...");
  213. load_sprite(&logo, LOGO);
  214. logo.alpha = ALPHA_EMBEDDED;
  215. TRACE("... done.");
  216. /* Generate surface for background */
  217. sprite_t * bg_sprite;
  218. int width = y->display_width;
  219. int height = y->display_height;
  220. int skip_animation = 0;
  221. /* Do something with a window */
  222. TRACE("Connecting to window server...");
  223. yutani_window_t * wina = yutani_window_create(y, width, height);
  224. assert(wina);
  225. yutani_set_stack(y, wina, 0);
  226. ctx = init_graphics_yutani_double_buffer(wina);
  227. draw_fill(ctx, rgba(0,0,0,255));
  228. yutani_flip(y, wina);
  229. TRACE("... done.");
  230. redo_everything:
  231. win_width = width;
  232. win_height = height;
  233. #if 0
  234. cairo_surface_t * cs = cairo_image_surface_create_for_data((void*)ctx->backbuffer, CAIRO_FORMAT_ARGB32, ctx->width, ctx->height, cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, ctx->width));
  235. cairo_t * cr = cairo_create(cs);
  236. #endif
  237. TRACE("Loading wallpaper...");
  238. {
  239. sprite_t * wallpaper = malloc(sizeof(sprite_t));
  240. load_sprite_jpg(wallpaper, WALLPAPER);
  241. float x = (float)width / (float)wallpaper->width;
  242. float y = (float)height / (float)wallpaper->height;
  243. int nh = (int)(x * (float)wallpaper->height);
  244. int nw = (int)(y * (float)wallpaper->width);;
  245. bg_sprite = create_sprite(width, height, ALPHA_OPAQUE);
  246. gfx_context_t * bg = init_graphics_sprite(bg_sprite);
  247. if (nw > width) {
  248. draw_sprite_scaled(bg, wallpaper, (width - nw) / 2, 0, nw, height);
  249. } else {
  250. draw_sprite_scaled(bg, wallpaper, 0, (height - nh) / 2, width, nh);
  251. }
  252. /* Three box blurs = good enough approximation of a guassian, but faster*/
  253. blur_context_box(bg, 20);
  254. blur_context_box(bg, 20);
  255. blur_context_box(bg, 20);
  256. free(bg);
  257. free(wallpaper);
  258. }
  259. TRACE("... done.");
  260. while (1) {
  261. yutani_set_stack(y, wina, 0);
  262. yutani_focus_window(y, wina->wid);
  263. draw_fill(ctx, rgb(0,0,0));
  264. draw_sprite(ctx, bg_sprite, center_x(width), center_y(height));
  265. flip(ctx);
  266. yutani_flip(y, wina);
  267. char * foo = malloc(sizeof(uint32_t) * width * height);
  268. memcpy(foo, ctx->backbuffer, sizeof(uint32_t) * width * height);
  269. TRACE("Begin animation.");
  270. if (!skip_animation) {
  271. struct timeval start;
  272. gettimeofday(&start, NULL);
  273. while (1) {
  274. uint32_t tick;
  275. struct timeval t;
  276. gettimeofday(&t, NULL);
  277. uint32_t sec_diff = t.tv_sec - start.tv_sec;
  278. uint32_t usec_diff = t.tv_usec - start.tv_usec;
  279. if (t.tv_usec < start.tv_usec) {
  280. sec_diff -= 1;
  281. usec_diff = (1000000 + t.tv_usec) - start.tv_usec;
  282. }
  283. tick = (uint32_t)(sec_diff * 1000 + usec_diff / 1000);
  284. int i = (float)LOGO_FINAL_OFFSET * (float)tick / 700.0f;
  285. if (i >= LOGO_FINAL_OFFSET) break;
  286. memcpy(ctx->backbuffer, foo, sizeof(uint32_t) * width * height);
  287. draw_sprite(ctx, &logo, center_x(logo.width), center_y(logo.height) - i);
  288. flip(ctx);
  289. yutani_flip_region(y, wina, center_x(logo.width), center_y(logo.height) - i, logo.width, logo.height + 5);
  290. usleep(10000);
  291. }
  292. }
  293. TRACE("End animation.");
  294. skip_animation = 0;
  295. char username[INPUT_SIZE] = {0};
  296. char password[INPUT_SIZE] = {0};
  297. char hostname[512];
  298. // we do it here to calculate the final string position
  299. get_updated_hostname_with_time_info(hostname);
  300. char kernel_v[512];
  301. {
  302. struct utsname u;
  303. uname(&u);
  304. char * os_name_ = "ToaruOS";
  305. snprintf(kernel_v, 512, "%s %s", os_name_, u.release);
  306. }
  307. uid = 0;
  308. int box_x, box_y;
  309. if (CENTER_BOX_X) {
  310. box_x = center_x(BOX_WIDTH);
  311. } else if (BOX_LEFT == -1) {
  312. box_x = win_width - BOX_RIGHT - BOX_WIDTH;
  313. } else {
  314. box_x = BOX_LEFT;
  315. }
  316. if (CENTER_BOX_Y) {
  317. box_y = center_y(0) + 8;
  318. } else if (BOX_TOP == -1) {
  319. box_y = win_width - BOX_BOTTOM - BOX_HEIGHT;
  320. } else {
  321. box_y = BOX_TOP;
  322. }
  323. int focus = 0;
  324. //set_font_size(11);
  325. int hostname_label_left = width - 10 - draw_sdf_string_width(hostname, 14, SDF_FONT_BOLD);
  326. int kernel_v_label_left = 10;
  327. struct text_box username_box = { (BOX_WIDTH - 170) / 2, 30, 170, 20, rgb(0,0,0), NULL, 0, 0, 0, username, "Username" };
  328. struct text_box password_box = { (BOX_WIDTH - 170) / 2, 58, 170, 20, rgb(0,0,0), NULL, 0, 1, 0, password, "Password" };
  329. struct login_container lc = { box_x, box_y, BOX_WIDTH, BOX_HEIGHT, &username_box, &password_box, 0 };
  330. username_box.parent = &lc;
  331. password_box.parent = &lc;
  332. while (1) {
  333. focus = 0;
  334. memset(username, 0x0, INPUT_SIZE);
  335. memset(password, 0x0, INPUT_SIZE);
  336. while (1) {
  337. // update time info
  338. get_updated_hostname_with_time_info(hostname);
  339. memcpy(ctx->backbuffer, foo, sizeof(uint32_t) * width * height);
  340. draw_sprite(ctx, &logo, center_x(logo.width), center_y(logo.height) - LOGO_FINAL_OFFSET);
  341. draw_sdf_string_shadow(ctx, hostname, 14, hostname_label_left, height - 22, rgb(255,255,255), rgb(0,0,0), 4, SDF_FONT_BOLD);
  342. draw_sdf_string_shadow(ctx, kernel_v, 14, kernel_v_label_left, height - 22, rgb(255,255,255), rgb(0,0,0), 4, SDF_FONT_BOLD);
  343. if (focus == USERNAME_BOX) {
  344. username_box.is_focused = 1;
  345. password_box.is_focused = 0;
  346. } else if (focus == PASSWORD_BOX) {
  347. username_box.is_focused = 0;
  348. password_box.is_focused = 1;
  349. } else {
  350. username_box.is_focused = 0;
  351. password_box.is_focused = 0;
  352. }
  353. draw_login_container(ctx, &lc);
  354. flip(ctx);
  355. yutani_flip(y, wina);
  356. struct yutani_msg_key_event kbd;
  357. struct yutani_msg_window_mouse_event mou;
  358. int msg_type = 0;
  359. collect_events:
  360. do {
  361. yutani_msg_t * msg = yutani_poll(y);
  362. switch (msg->type) {
  363. case YUTANI_MSG_KEY_EVENT:
  364. {
  365. struct yutani_msg_key_event * ke = (void*)msg->data;
  366. if (ke->event.action == KEY_ACTION_DOWN) {
  367. memcpy(&kbd, ke, sizeof(struct yutani_msg_key_event));
  368. msg_type = 1;
  369. }
  370. }
  371. break;
  372. case YUTANI_MSG_WINDOW_MOUSE_EVENT:
  373. {
  374. struct yutani_msg_window_mouse_event * me = (void*)msg->data;
  375. memcpy(&mou, me, sizeof(struct yutani_msg_mouse_event));
  376. msg_type = 2;
  377. }
  378. break;
  379. case YUTANI_MSG_WELCOME:
  380. {
  381. struct yutani_msg_welcome * mw = (void*)msg->data;
  382. yutani_window_resize(y, wina, mw->display_width, mw->display_height);
  383. }
  384. break;
  385. case YUTANI_MSG_RESIZE_OFFER:
  386. {
  387. struct yutani_msg_window_resize * wr = (void*)msg->data;
  388. width = wr->width;
  389. height = wr->height;
  390. yutani_window_resize_accept(y, wina, width, height);
  391. reinit_graphics_yutani(ctx, wina);
  392. yutani_window_resize_done(y, wina);
  393. sprite_free(bg_sprite);
  394. //cairo_destroy(cr);
  395. //cairo_surface_destroy(cs);
  396. skip_animation = 1;
  397. goto redo_everything;
  398. }
  399. break;
  400. }
  401. free(msg);
  402. } while (!msg_type);
  403. if (msg_type == 1) {
  404. if (kbd.event.keycode == '\n') {
  405. if (focus == USERNAME_BOX) {
  406. focus = PASSWORD_BOX;
  407. continue;
  408. } else if (focus == PASSWORD_BOX) {
  409. break;
  410. } else {
  411. focus = USERNAME_BOX;
  412. continue;
  413. }
  414. }
  415. if (kbd.event.keycode == '\t') {
  416. if (focus == USERNAME_BOX) {
  417. focus = PASSWORD_BOX;
  418. } else {
  419. focus = USERNAME_BOX;
  420. }
  421. continue;
  422. }
  423. if (kbd.event.key) {
  424. if (!focus) {
  425. focus = USERNAME_BOX;
  426. }
  427. if (focus == USERNAME_BOX) {
  428. buffer_put(username, kbd.event.key);
  429. } else if (focus == PASSWORD_BOX) {
  430. buffer_put(password, kbd.event.key);
  431. }
  432. }
  433. } else if (msg_type == 2) {
  434. if ((mou.command == YUTANI_MOUSE_EVENT_DOWN
  435. && mou.buttons & YUTANI_MOUSE_BUTTON_LEFT)
  436. || (mou.command == YUTANI_MOUSE_EVENT_CLICK)) {
  437. /* Determine if we were inside of a text box */
  438. if (mou.new_x >= (int)lc.x + (int)username_box.x &&
  439. mou.new_x <= (int)lc.x + (int)username_box.x + (int)username_box.width &&
  440. mou.new_y >= (int)lc.y + (int)username_box.y &&
  441. mou.new_y <= (int)lc.y + (int)username_box.y + (int)username_box.height) {
  442. /* Ensure this box is focused. */
  443. focus = USERNAME_BOX;
  444. continue;
  445. } else if (
  446. (int)mou.new_x >= (int)lc.x + (int)password_box.x &&
  447. (int)mou.new_x <= (int)lc.x + (int)password_box.x + (int)password_box.width &&
  448. (int)mou.new_y >= (int)lc.y + (int)password_box.y &&
  449. (int)mou.new_y <= (int)lc.y + (int)password_box.y + (int)password_box.height) {
  450. /* Ensure this box is focused. */
  451. focus = PASSWORD_BOX;
  452. continue;
  453. } else {
  454. focus = 0;
  455. continue;
  456. }
  457. } else {
  458. goto collect_events;
  459. }
  460. }
  461. }
  462. fprintf(stdout, "USER %s\n", username);
  463. fprintf(stdout, "PASS %s\n", password);
  464. fprintf(stdout, "AUTH\n");
  465. char tmp[1024];
  466. fgets(tmp, 1024, stdin);
  467. if (!strcmp(tmp,"FAIL\n")) {
  468. lc.show_error = 1;
  469. continue;
  470. } else if (!strcmp(tmp,"SUCC\n")) {
  471. fprintf(stderr,"Success!\n");
  472. goto _success;
  473. }
  474. }
  475. }
  476. _success:
  477. yutani_close(y, wina);
  478. return 0;
  479. }