sdl2.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  1. /*
  2. * QEMU SDL display driver
  3. *
  4. * Copyright (c) 2003 Fabrice Bellard
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. /* Ported SDL 1.2 code to 2.0 by Dave Airlie. */
  25. #include "qemu/osdep.h"
  26. #include "qemu-common.h"
  27. #include "ui/console.h"
  28. #include "ui/input.h"
  29. #include "ui/sdl2.h"
  30. #include "sysemu/sysemu.h"
  31. static int sdl2_num_outputs;
  32. static struct sdl2_console *sdl2_console;
  33. static SDL_Surface *guest_sprite_surface;
  34. static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
  35. static int gui_saved_grab;
  36. static int gui_fullscreen;
  37. static int gui_noframe;
  38. static int gui_key_modifier_pressed;
  39. static int gui_keysym;
  40. static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
  41. static SDL_Cursor *sdl_cursor_normal;
  42. static SDL_Cursor *sdl_cursor_hidden;
  43. static int absolute_enabled;
  44. static int guest_cursor;
  45. static int guest_x, guest_y;
  46. static SDL_Cursor *guest_sprite;
  47. static Notifier mouse_mode_notifier;
  48. #define SDL2_REFRESH_INTERVAL_BUSY 10
  49. #define SDL2_MAX_IDLE_COUNT (2 * GUI_REFRESH_INTERVAL_DEFAULT \
  50. / SDL2_REFRESH_INTERVAL_BUSY + 1)
  51. static void sdl_update_caption(struct sdl2_console *scon);
  52. static struct sdl2_console *get_scon_from_window(uint32_t window_id)
  53. {
  54. int i;
  55. for (i = 0; i < sdl2_num_outputs; i++) {
  56. if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id)) {
  57. return &sdl2_console[i];
  58. }
  59. }
  60. return NULL;
  61. }
  62. void sdl2_window_create(struct sdl2_console *scon)
  63. {
  64. int flags = 0;
  65. if (!scon->surface) {
  66. return;
  67. }
  68. assert(!scon->real_window);
  69. if (gui_fullscreen) {
  70. flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
  71. } else {
  72. flags |= SDL_WINDOW_RESIZABLE;
  73. }
  74. if (scon->hidden) {
  75. flags |= SDL_WINDOW_HIDDEN;
  76. }
  77. scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED,
  78. SDL_WINDOWPOS_UNDEFINED,
  79. surface_width(scon->surface),
  80. surface_height(scon->surface),
  81. flags);
  82. scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
  83. if (scon->opengl) {
  84. scon->winctx = SDL_GL_GetCurrentContext();
  85. }
  86. sdl_update_caption(scon);
  87. }
  88. void sdl2_window_destroy(struct sdl2_console *scon)
  89. {
  90. if (!scon->real_window) {
  91. return;
  92. }
  93. SDL_DestroyRenderer(scon->real_renderer);
  94. scon->real_renderer = NULL;
  95. SDL_DestroyWindow(scon->real_window);
  96. scon->real_window = NULL;
  97. }
  98. void sdl2_window_resize(struct sdl2_console *scon)
  99. {
  100. if (!scon->real_window) {
  101. return;
  102. }
  103. SDL_SetWindowSize(scon->real_window,
  104. surface_width(scon->surface),
  105. surface_height(scon->surface));
  106. }
  107. static void sdl2_redraw(struct sdl2_console *scon)
  108. {
  109. if (scon->opengl) {
  110. #ifdef CONFIG_OPENGL
  111. sdl2_gl_redraw(scon);
  112. #endif
  113. } else {
  114. sdl2_2d_redraw(scon);
  115. }
  116. }
  117. static void sdl_update_caption(struct sdl2_console *scon)
  118. {
  119. char win_title[1024];
  120. char icon_title[1024];
  121. const char *status = "";
  122. if (!runstate_is_running()) {
  123. status = " [Stopped]";
  124. } else if (gui_grab) {
  125. if (alt_grab) {
  126. status = " - Press Ctrl-Alt-Shift to exit grab";
  127. } else if (ctrl_grab) {
  128. status = " - Press Right-Ctrl to exit grab";
  129. } else {
  130. status = " - Press Ctrl-Alt to exit grab";
  131. }
  132. }
  133. if (qemu_name) {
  134. snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name,
  135. scon->idx, status);
  136. snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
  137. } else {
  138. snprintf(win_title, sizeof(win_title), "QEMU%s", status);
  139. snprintf(icon_title, sizeof(icon_title), "QEMU");
  140. }
  141. if (scon->real_window) {
  142. SDL_SetWindowTitle(scon->real_window, win_title);
  143. }
  144. }
  145. static void sdl_hide_cursor(void)
  146. {
  147. if (!cursor_hide) {
  148. return;
  149. }
  150. if (qemu_input_is_absolute()) {
  151. SDL_ShowCursor(1);
  152. SDL_SetCursor(sdl_cursor_hidden);
  153. } else {
  154. SDL_SetRelativeMouseMode(SDL_TRUE);
  155. }
  156. }
  157. static void sdl_show_cursor(void)
  158. {
  159. if (!cursor_hide) {
  160. return;
  161. }
  162. if (!qemu_input_is_absolute()) {
  163. SDL_SetRelativeMouseMode(SDL_FALSE);
  164. SDL_ShowCursor(1);
  165. if (guest_cursor &&
  166. (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
  167. SDL_SetCursor(guest_sprite);
  168. } else {
  169. SDL_SetCursor(sdl_cursor_normal);
  170. }
  171. }
  172. }
  173. static void sdl_grab_start(struct sdl2_console *scon)
  174. {
  175. QemuConsole *con = scon ? scon->dcl.con : NULL;
  176. if (!con || !qemu_console_is_graphic(con)) {
  177. return;
  178. }
  179. /*
  180. * If the application is not active, do not try to enter grab state. This
  181. * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
  182. * application (SDL bug).
  183. */
  184. if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) {
  185. return;
  186. }
  187. if (guest_cursor) {
  188. SDL_SetCursor(guest_sprite);
  189. if (!qemu_input_is_absolute() && !absolute_enabled) {
  190. SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y);
  191. }
  192. } else {
  193. sdl_hide_cursor();
  194. }
  195. SDL_SetWindowGrab(scon->real_window, SDL_TRUE);
  196. gui_grab = 1;
  197. sdl_update_caption(scon);
  198. }
  199. static void sdl_grab_end(struct sdl2_console *scon)
  200. {
  201. SDL_SetWindowGrab(scon->real_window, SDL_FALSE);
  202. gui_grab = 0;
  203. sdl_show_cursor();
  204. sdl_update_caption(scon);
  205. }
  206. static void absolute_mouse_grab(struct sdl2_console *scon)
  207. {
  208. int mouse_x, mouse_y;
  209. int scr_w, scr_h;
  210. SDL_GetMouseState(&mouse_x, &mouse_y);
  211. SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
  212. if (mouse_x > 0 && mouse_x < scr_w - 1 &&
  213. mouse_y > 0 && mouse_y < scr_h - 1) {
  214. sdl_grab_start(scon);
  215. }
  216. }
  217. static void sdl_mouse_mode_change(Notifier *notify, void *data)
  218. {
  219. if (qemu_input_is_absolute()) {
  220. if (!absolute_enabled) {
  221. absolute_enabled = 1;
  222. absolute_mouse_grab(&sdl2_console[0]);
  223. }
  224. } else if (absolute_enabled) {
  225. if (!gui_fullscreen) {
  226. sdl_grab_end(&sdl2_console[0]);
  227. }
  228. absolute_enabled = 0;
  229. }
  230. }
  231. static void sdl_send_mouse_event(struct sdl2_console *scon, int dx, int dy,
  232. int x, int y, int state)
  233. {
  234. static uint32_t bmap[INPUT_BUTTON__MAX] = {
  235. [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT),
  236. [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE),
  237. [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT),
  238. };
  239. static uint32_t prev_state;
  240. if (prev_state != state) {
  241. qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state);
  242. prev_state = state;
  243. }
  244. if (qemu_input_is_absolute()) {
  245. int scr_w, scr_h;
  246. int max_w = 0, max_h = 0;
  247. int off_x = 0, off_y = 0;
  248. int cur_off_x = 0, cur_off_y = 0;
  249. int i;
  250. for (i = 0; i < sdl2_num_outputs; i++) {
  251. struct sdl2_console *thiscon = &sdl2_console[i];
  252. if (thiscon->real_window && thiscon->surface) {
  253. SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h);
  254. cur_off_x = thiscon->x;
  255. cur_off_y = thiscon->y;
  256. if (scr_w + cur_off_x > max_w) {
  257. max_w = scr_w + cur_off_x;
  258. }
  259. if (scr_h + cur_off_y > max_h) {
  260. max_h = scr_h + cur_off_y;
  261. }
  262. if (i == scon->idx) {
  263. off_x = cur_off_x;
  264. off_y = cur_off_y;
  265. }
  266. }
  267. }
  268. qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, 0, max_w);
  269. qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, 0, max_h);
  270. } else {
  271. if (guest_cursor) {
  272. x -= guest_x;
  273. y -= guest_y;
  274. guest_x += x;
  275. guest_y += y;
  276. dx = x;
  277. dy = y;
  278. }
  279. qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, dx);
  280. qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, dy);
  281. }
  282. qemu_input_event_sync();
  283. }
  284. static void toggle_full_screen(struct sdl2_console *scon)
  285. {
  286. gui_fullscreen = !gui_fullscreen;
  287. if (gui_fullscreen) {
  288. SDL_SetWindowFullscreen(scon->real_window,
  289. SDL_WINDOW_FULLSCREEN_DESKTOP);
  290. gui_saved_grab = gui_grab;
  291. sdl_grab_start(scon);
  292. } else {
  293. if (!gui_saved_grab) {
  294. sdl_grab_end(scon);
  295. }
  296. SDL_SetWindowFullscreen(scon->real_window, 0);
  297. }
  298. sdl2_redraw(scon);
  299. }
  300. static void handle_keydown(SDL_Event *ev)
  301. {
  302. int mod_state, win;
  303. struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
  304. if (alt_grab) {
  305. mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
  306. (gui_grab_code | KMOD_LSHIFT);
  307. } else if (ctrl_grab) {
  308. mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
  309. } else {
  310. mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code;
  311. }
  312. gui_key_modifier_pressed = mod_state;
  313. if (gui_key_modifier_pressed) {
  314. switch (ev->key.keysym.scancode) {
  315. case SDL_SCANCODE_2:
  316. case SDL_SCANCODE_3:
  317. case SDL_SCANCODE_4:
  318. case SDL_SCANCODE_5:
  319. case SDL_SCANCODE_6:
  320. case SDL_SCANCODE_7:
  321. case SDL_SCANCODE_8:
  322. case SDL_SCANCODE_9:
  323. if (gui_grab) {
  324. sdl_grab_end(scon);
  325. }
  326. win = ev->key.keysym.scancode - SDL_SCANCODE_1;
  327. if (win < sdl2_num_outputs) {
  328. sdl2_console[win].hidden = !sdl2_console[win].hidden;
  329. if (sdl2_console[win].real_window) {
  330. if (sdl2_console[win].hidden) {
  331. SDL_HideWindow(sdl2_console[win].real_window);
  332. } else {
  333. SDL_ShowWindow(sdl2_console[win].real_window);
  334. }
  335. }
  336. gui_keysym = 1;
  337. }
  338. break;
  339. case SDL_SCANCODE_F:
  340. toggle_full_screen(scon);
  341. gui_keysym = 1;
  342. break;
  343. case SDL_SCANCODE_U:
  344. sdl2_window_destroy(scon);
  345. sdl2_window_create(scon);
  346. if (!scon->opengl) {
  347. /* re-create scon->texture */
  348. sdl2_2d_switch(&scon->dcl, scon->surface);
  349. }
  350. gui_keysym = 1;
  351. break;
  352. #if 0
  353. case SDL_SCANCODE_KP_PLUS:
  354. case SDL_SCANCODE_KP_MINUS:
  355. if (!gui_fullscreen) {
  356. int scr_w, scr_h;
  357. int width, height;
  358. SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
  359. width = MAX(scr_w + (ev->key.keysym.scancode ==
  360. SDL_SCANCODE_KP_PLUS ? 50 : -50),
  361. 160);
  362. height = (surface_height(scon->surface) * width) /
  363. surface_width(scon->surface);
  364. fprintf(stderr, "%s: scale to %dx%d\n",
  365. __func__, width, height);
  366. sdl_scale(scon, width, height);
  367. sdl2_redraw(scon);
  368. gui_keysym = 1;
  369. }
  370. #endif
  371. default:
  372. break;
  373. }
  374. }
  375. if (!gui_keysym) {
  376. sdl2_process_key(scon, &ev->key);
  377. }
  378. }
  379. static void handle_keyup(SDL_Event *ev)
  380. {
  381. int mod_state;
  382. struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
  383. if (!alt_grab) {
  384. mod_state = (ev->key.keysym.mod & gui_grab_code);
  385. } else {
  386. mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT));
  387. }
  388. if (!mod_state && gui_key_modifier_pressed) {
  389. gui_key_modifier_pressed = 0;
  390. if (gui_keysym == 0) {
  391. /* exit/enter grab if pressing Ctrl-Alt */
  392. if (!gui_grab) {
  393. sdl_grab_start(scon);
  394. } else if (!gui_fullscreen) {
  395. sdl_grab_end(scon);
  396. }
  397. /* SDL does not send back all the modifiers key, so we must
  398. * correct it. */
  399. sdl2_reset_keys(scon);
  400. return;
  401. }
  402. gui_keysym = 0;
  403. }
  404. if (!gui_keysym) {
  405. sdl2_process_key(scon, &ev->key);
  406. }
  407. }
  408. static void handle_textinput(SDL_Event *ev)
  409. {
  410. struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
  411. QemuConsole *con = scon ? scon->dcl.con : NULL;
  412. if (qemu_console_is_graphic(con)) {
  413. return;
  414. }
  415. kbd_put_string_console(con, ev->text.text, strlen(ev->text.text));
  416. }
  417. static void handle_mousemotion(SDL_Event *ev)
  418. {
  419. int max_x, max_y;
  420. struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
  421. if (qemu_input_is_absolute() || absolute_enabled) {
  422. int scr_w, scr_h;
  423. SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
  424. max_x = scr_w - 1;
  425. max_y = scr_h - 1;
  426. if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
  427. ev->motion.x == max_x || ev->motion.y == max_y)) {
  428. sdl_grab_end(scon);
  429. }
  430. if (!gui_grab &&
  431. (ev->motion.x > 0 && ev->motion.x < max_x &&
  432. ev->motion.y > 0 && ev->motion.y < max_y)) {
  433. sdl_grab_start(scon);
  434. }
  435. }
  436. if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
  437. sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel,
  438. ev->motion.x, ev->motion.y, ev->motion.state);
  439. }
  440. }
  441. static void handle_mousebutton(SDL_Event *ev)
  442. {
  443. int buttonstate = SDL_GetMouseState(NULL, NULL);
  444. SDL_MouseButtonEvent *bev;
  445. struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
  446. bev = &ev->button;
  447. if (!gui_grab && !qemu_input_is_absolute()) {
  448. if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
  449. /* start grabbing all events */
  450. sdl_grab_start(scon);
  451. }
  452. } else {
  453. if (ev->type == SDL_MOUSEBUTTONDOWN) {
  454. buttonstate |= SDL_BUTTON(bev->button);
  455. } else {
  456. buttonstate &= ~SDL_BUTTON(bev->button);
  457. }
  458. sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate);
  459. }
  460. }
  461. static void handle_mousewheel(SDL_Event *ev)
  462. {
  463. struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
  464. SDL_MouseWheelEvent *wev = &ev->wheel;
  465. InputButton btn;
  466. if (wev->y > 0) {
  467. btn = INPUT_BUTTON_WHEEL_UP;
  468. } else if (wev->y < 0) {
  469. btn = INPUT_BUTTON_WHEEL_DOWN;
  470. } else {
  471. return;
  472. }
  473. qemu_input_queue_btn(scon->dcl.con, btn, true);
  474. qemu_input_event_sync();
  475. qemu_input_queue_btn(scon->dcl.con, btn, false);
  476. qemu_input_event_sync();
  477. }
  478. static void handle_windowevent(SDL_Event *ev)
  479. {
  480. struct sdl2_console *scon = get_scon_from_window(ev->window.windowID);
  481. if (!scon) {
  482. return;
  483. }
  484. switch (ev->window.event) {
  485. case SDL_WINDOWEVENT_RESIZED:
  486. {
  487. QemuUIInfo info;
  488. memset(&info, 0, sizeof(info));
  489. info.width = ev->window.data1;
  490. info.height = ev->window.data2;
  491. dpy_set_ui_info(scon->dcl.con, &info);
  492. }
  493. sdl2_redraw(scon);
  494. break;
  495. case SDL_WINDOWEVENT_EXPOSED:
  496. sdl2_redraw(scon);
  497. break;
  498. case SDL_WINDOWEVENT_FOCUS_GAINED:
  499. case SDL_WINDOWEVENT_ENTER:
  500. if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) {
  501. absolute_mouse_grab(scon);
  502. }
  503. break;
  504. case SDL_WINDOWEVENT_FOCUS_LOST:
  505. if (gui_grab && !gui_fullscreen) {
  506. sdl_grab_end(scon);
  507. }
  508. break;
  509. case SDL_WINDOWEVENT_RESTORED:
  510. update_displaychangelistener(&scon->dcl, GUI_REFRESH_INTERVAL_DEFAULT);
  511. break;
  512. case SDL_WINDOWEVENT_MINIMIZED:
  513. update_displaychangelistener(&scon->dcl, 500);
  514. break;
  515. case SDL_WINDOWEVENT_CLOSE:
  516. if (!no_quit) {
  517. no_shutdown = 0;
  518. qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI);
  519. }
  520. break;
  521. case SDL_WINDOWEVENT_SHOWN:
  522. if (scon->hidden) {
  523. SDL_HideWindow(scon->real_window);
  524. }
  525. break;
  526. case SDL_WINDOWEVENT_HIDDEN:
  527. if (!scon->hidden) {
  528. SDL_ShowWindow(scon->real_window);
  529. }
  530. break;
  531. }
  532. }
  533. void sdl2_poll_events(struct sdl2_console *scon)
  534. {
  535. SDL_Event ev1, *ev = &ev1;
  536. int idle = 1;
  537. if (scon->last_vm_running != runstate_is_running()) {
  538. scon->last_vm_running = runstate_is_running();
  539. sdl_update_caption(scon);
  540. }
  541. while (SDL_PollEvent(ev)) {
  542. switch (ev->type) {
  543. case SDL_KEYDOWN:
  544. idle = 0;
  545. handle_keydown(ev);
  546. break;
  547. case SDL_KEYUP:
  548. idle = 0;
  549. handle_keyup(ev);
  550. break;
  551. case SDL_TEXTINPUT:
  552. idle = 0;
  553. handle_textinput(ev);
  554. break;
  555. case SDL_QUIT:
  556. if (!no_quit) {
  557. no_shutdown = 0;
  558. qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI);
  559. }
  560. break;
  561. case SDL_MOUSEMOTION:
  562. idle = 0;
  563. handle_mousemotion(ev);
  564. break;
  565. case SDL_MOUSEBUTTONDOWN:
  566. case SDL_MOUSEBUTTONUP:
  567. idle = 0;
  568. handle_mousebutton(ev);
  569. break;
  570. case SDL_MOUSEWHEEL:
  571. idle = 0;
  572. handle_mousewheel(ev);
  573. break;
  574. case SDL_WINDOWEVENT:
  575. handle_windowevent(ev);
  576. break;
  577. default:
  578. break;
  579. }
  580. }
  581. if (idle) {
  582. if (scon->idle_counter < SDL2_MAX_IDLE_COUNT) {
  583. scon->idle_counter++;
  584. if (scon->idle_counter >= SDL2_MAX_IDLE_COUNT) {
  585. scon->dcl.update_interval = GUI_REFRESH_INTERVAL_DEFAULT;
  586. }
  587. }
  588. } else {
  589. scon->idle_counter = 0;
  590. scon->dcl.update_interval = SDL2_REFRESH_INTERVAL_BUSY;
  591. }
  592. }
  593. static void sdl_mouse_warp(DisplayChangeListener *dcl,
  594. int x, int y, int on)
  595. {
  596. struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
  597. if (on) {
  598. if (!guest_cursor) {
  599. sdl_show_cursor();
  600. }
  601. if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
  602. SDL_SetCursor(guest_sprite);
  603. if (!qemu_input_is_absolute() && !absolute_enabled) {
  604. SDL_WarpMouseInWindow(scon->real_window, x, y);
  605. }
  606. }
  607. } else if (gui_grab) {
  608. sdl_hide_cursor();
  609. }
  610. guest_cursor = on;
  611. guest_x = x, guest_y = y;
  612. }
  613. static void sdl_mouse_define(DisplayChangeListener *dcl,
  614. QEMUCursor *c)
  615. {
  616. if (guest_sprite) {
  617. SDL_FreeCursor(guest_sprite);
  618. }
  619. if (guest_sprite_surface) {
  620. SDL_FreeSurface(guest_sprite_surface);
  621. }
  622. guest_sprite_surface =
  623. SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4,
  624. 0xff0000, 0x00ff00, 0xff, 0xff000000);
  625. if (!guest_sprite_surface) {
  626. fprintf(stderr, "Failed to make rgb surface from %p\n", c);
  627. return;
  628. }
  629. guest_sprite = SDL_CreateColorCursor(guest_sprite_surface,
  630. c->hot_x, c->hot_y);
  631. if (!guest_sprite) {
  632. fprintf(stderr, "Failed to make color cursor from %p\n", c);
  633. return;
  634. }
  635. if (guest_cursor &&
  636. (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
  637. SDL_SetCursor(guest_sprite);
  638. }
  639. }
  640. static void sdl_cleanup(void)
  641. {
  642. if (guest_sprite) {
  643. SDL_FreeCursor(guest_sprite);
  644. }
  645. SDL_QuitSubSystem(SDL_INIT_VIDEO);
  646. }
  647. static const DisplayChangeListenerOps dcl_2d_ops = {
  648. .dpy_name = "sdl2-2d",
  649. .dpy_gfx_update = sdl2_2d_update,
  650. .dpy_gfx_switch = sdl2_2d_switch,
  651. .dpy_gfx_check_format = sdl2_2d_check_format,
  652. .dpy_refresh = sdl2_2d_refresh,
  653. .dpy_mouse_set = sdl_mouse_warp,
  654. .dpy_cursor_define = sdl_mouse_define,
  655. };
  656. #ifdef CONFIG_OPENGL
  657. static const DisplayChangeListenerOps dcl_gl_ops = {
  658. .dpy_name = "sdl2-gl",
  659. .dpy_gfx_update = sdl2_gl_update,
  660. .dpy_gfx_switch = sdl2_gl_switch,
  661. .dpy_gfx_check_format = console_gl_check_format,
  662. .dpy_refresh = sdl2_gl_refresh,
  663. .dpy_mouse_set = sdl_mouse_warp,
  664. .dpy_cursor_define = sdl_mouse_define,
  665. .dpy_gl_ctx_create = sdl2_gl_create_context,
  666. .dpy_gl_ctx_destroy = sdl2_gl_destroy_context,
  667. .dpy_gl_ctx_make_current = sdl2_gl_make_context_current,
  668. .dpy_gl_ctx_get_current = sdl2_gl_get_current_context,
  669. .dpy_gl_scanout_disable = sdl2_gl_scanout_disable,
  670. .dpy_gl_scanout_texture = sdl2_gl_scanout_texture,
  671. .dpy_gl_update = sdl2_gl_scanout_flush,
  672. };
  673. #endif
  674. void sdl_display_early_init(int opengl)
  675. {
  676. switch (opengl) {
  677. case -1: /* default */
  678. case 0: /* off */
  679. break;
  680. case 1: /* on */
  681. #ifdef CONFIG_OPENGL
  682. display_opengl = 1;
  683. #endif
  684. break;
  685. default:
  686. g_assert_not_reached();
  687. break;
  688. }
  689. }
  690. void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
  691. {
  692. int flags;
  693. uint8_t data = 0;
  694. char *filename;
  695. int i;
  696. SDL_SysWMinfo info;
  697. if (no_frame) {
  698. gui_noframe = 1;
  699. }
  700. #ifdef __linux__
  701. /* on Linux, SDL may use fbcon|directfb|svgalib when run without
  702. * accessible $DISPLAY to open X11 window. This is often the case
  703. * when qemu is run using sudo. But in this case, and when actually
  704. * run in X11 environment, SDL fights with X11 for the video card,
  705. * making current display unavailable, often until reboot.
  706. * So make x11 the default SDL video driver if this variable is unset.
  707. * This is a bit hackish but saves us from bigger problem.
  708. * Maybe it's a good idea to fix this in SDL instead.
  709. */
  710. setenv("SDL_VIDEODRIVER", "x11", 0);
  711. #endif
  712. flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
  713. if (SDL_Init(flags)) {
  714. fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
  715. SDL_GetError());
  716. exit(1);
  717. }
  718. SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1");
  719. memset(&info, 0, sizeof(info));
  720. SDL_VERSION(&info.version);
  721. for (i = 0;; i++) {
  722. QemuConsole *con = qemu_console_lookup_by_index(i);
  723. if (!con) {
  724. break;
  725. }
  726. }
  727. sdl2_num_outputs = i;
  728. if (sdl2_num_outputs == 0) {
  729. return;
  730. }
  731. sdl2_console = g_new0(struct sdl2_console, sdl2_num_outputs);
  732. for (i = 0; i < sdl2_num_outputs; i++) {
  733. QemuConsole *con = qemu_console_lookup_by_index(i);
  734. if (!qemu_console_is_graphic(con)) {
  735. sdl2_console[i].hidden = true;
  736. }
  737. sdl2_console[i].idx = i;
  738. #ifdef CONFIG_OPENGL
  739. sdl2_console[i].opengl = display_opengl;
  740. sdl2_console[i].dcl.ops = display_opengl ? &dcl_gl_ops : &dcl_2d_ops;
  741. #else
  742. sdl2_console[i].opengl = 0;
  743. sdl2_console[i].dcl.ops = &dcl_2d_ops;
  744. #endif
  745. sdl2_console[i].dcl.con = con;
  746. register_displaychangelistener(&sdl2_console[i].dcl);
  747. #if defined(SDL_VIDEO_DRIVER_WINDOWS) || defined(SDL_VIDEO_DRIVER_X11)
  748. if (SDL_GetWindowWMInfo(sdl2_console[i].real_window, &info)) {
  749. #if defined(SDL_VIDEO_DRIVER_WINDOWS)
  750. qemu_console_set_window_id(con, (uintptr_t)info.info.win.window);
  751. #elif defined(SDL_VIDEO_DRIVER_X11)
  752. qemu_console_set_window_id(con, info.info.x11.window);
  753. #endif
  754. }
  755. #endif
  756. }
  757. /* Load a 32x32x4 image. White pixels are transparent. */
  758. filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
  759. if (filename) {
  760. SDL_Surface *image = SDL_LoadBMP(filename);
  761. if (image) {
  762. uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
  763. SDL_SetColorKey(image, SDL_TRUE, colorkey);
  764. SDL_SetWindowIcon(sdl2_console[0].real_window, image);
  765. }
  766. g_free(filename);
  767. }
  768. if (full_screen) {
  769. gui_fullscreen = 1;
  770. sdl_grab_start(0);
  771. }
  772. mouse_mode_notifier.notify = sdl_mouse_mode_change;
  773. qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
  774. gui_grab = 0;
  775. sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
  776. sdl_cursor_normal = SDL_GetCursor();
  777. atexit(sdl_cleanup);
  778. }