sdl2.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915
  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. /* Avoid compiler warning because macro is redefined in SDL_syswm.h. */
  26. #undef WIN32_LEAN_AND_MEAN
  27. #include <SDL.h>
  28. #if SDL_MAJOR_VERSION == 2
  29. #include <SDL_syswm.h>
  30. #include "qemu-common.h"
  31. #include "ui/console.h"
  32. #include "ui/input.h"
  33. #include "sysemu/sysemu.h"
  34. #include "sdl2-keymap.h"
  35. static int sdl2_num_outputs;
  36. static struct sdl2_state {
  37. DisplayChangeListener dcl;
  38. DisplaySurface *surface;
  39. SDL_Texture *texture;
  40. SDL_Window *real_window;
  41. SDL_Renderer *real_renderer;
  42. int idx;
  43. int last_vm_running; /* per console for caption reasons */
  44. int x, y;
  45. int hidden;
  46. } *sdl2_console;
  47. static SDL_Surface *guest_sprite_surface;
  48. static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
  49. static bool gui_saved_scaling;
  50. static int gui_saved_width;
  51. static int gui_saved_height;
  52. static int gui_saved_grab;
  53. static int gui_fullscreen;
  54. static int gui_noframe;
  55. static int gui_key_modifier_pressed;
  56. static int gui_keysym;
  57. static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
  58. static uint8_t modifiers_state[SDL_NUM_SCANCODES];
  59. static SDL_Cursor *sdl_cursor_normal;
  60. static SDL_Cursor *sdl_cursor_hidden;
  61. static int absolute_enabled;
  62. static int guest_cursor;
  63. static int guest_x, guest_y;
  64. static SDL_Cursor *guest_sprite;
  65. static int scaling_active;
  66. static Notifier mouse_mode_notifier;
  67. static void sdl_update_caption(struct sdl2_state *scon);
  68. static struct sdl2_state *get_scon_from_window(uint32_t window_id)
  69. {
  70. int i;
  71. for (i = 0; i < sdl2_num_outputs; i++) {
  72. if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id)) {
  73. return &sdl2_console[i];
  74. }
  75. }
  76. return NULL;
  77. }
  78. static void sdl_update(DisplayChangeListener *dcl,
  79. int x, int y, int w, int h)
  80. {
  81. struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
  82. SDL_Rect rect;
  83. DisplaySurface *surf = qemu_console_surface(dcl->con);
  84. if (!surf) {
  85. return;
  86. }
  87. if (!scon->texture) {
  88. return;
  89. }
  90. rect.x = x;
  91. rect.y = y;
  92. rect.w = w;
  93. rect.h = h;
  94. SDL_UpdateTexture(scon->texture, NULL, surface_data(surf),
  95. surface_stride(surf));
  96. SDL_RenderCopy(scon->real_renderer, scon->texture, &rect, &rect);
  97. SDL_RenderPresent(scon->real_renderer);
  98. }
  99. static void do_sdl_resize(struct sdl2_state *scon, int width, int height,
  100. int bpp)
  101. {
  102. int flags;
  103. if (scon->real_window && scon->real_renderer) {
  104. if (width && height) {
  105. SDL_RenderSetLogicalSize(scon->real_renderer, width, height);
  106. SDL_SetWindowSize(scon->real_window, width, height);
  107. } else {
  108. SDL_DestroyRenderer(scon->real_renderer);
  109. SDL_DestroyWindow(scon->real_window);
  110. scon->real_renderer = NULL;
  111. scon->real_window = NULL;
  112. }
  113. } else {
  114. if (!width || !height) {
  115. return;
  116. }
  117. flags = 0;
  118. if (gui_fullscreen) {
  119. flags |= SDL_WINDOW_FULLSCREEN;
  120. } else {
  121. flags |= SDL_WINDOW_RESIZABLE;
  122. }
  123. if (scon->hidden) {
  124. flags |= SDL_WINDOW_HIDDEN;
  125. }
  126. scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED,
  127. SDL_WINDOWPOS_UNDEFINED,
  128. width, height, flags);
  129. scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
  130. sdl_update_caption(scon);
  131. }
  132. }
  133. static void sdl_switch(DisplayChangeListener *dcl,
  134. DisplaySurface *new_surface)
  135. {
  136. struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
  137. int format = 0;
  138. int idx = scon->idx;
  139. DisplaySurface *old_surface = scon->surface;
  140. /* temporary hack: allows to call sdl_switch to handle scaling changes */
  141. if (new_surface) {
  142. scon->surface = new_surface;
  143. }
  144. if (!new_surface && idx > 0) {
  145. scon->surface = NULL;
  146. }
  147. if (new_surface == NULL) {
  148. do_sdl_resize(scon, 0, 0, 0);
  149. } else {
  150. do_sdl_resize(scon, surface_width(scon->surface),
  151. surface_height(scon->surface), 0);
  152. }
  153. if (old_surface && scon->texture) {
  154. SDL_DestroyTexture(scon->texture);
  155. scon->texture = NULL;
  156. }
  157. if (new_surface) {
  158. if (!scon->texture) {
  159. if (surface_bits_per_pixel(scon->surface) == 16) {
  160. format = SDL_PIXELFORMAT_RGB565;
  161. } else if (surface_bits_per_pixel(scon->surface) == 32) {
  162. format = SDL_PIXELFORMAT_ARGB8888;
  163. }
  164. scon->texture = SDL_CreateTexture(scon->real_renderer, format,
  165. SDL_TEXTUREACCESS_STREAMING,
  166. surface_width(new_surface),
  167. surface_height(new_surface));
  168. }
  169. }
  170. }
  171. static void reset_keys(struct sdl2_state *scon)
  172. {
  173. QemuConsole *con = scon ? scon->dcl.con : NULL;
  174. int i;
  175. for (i = 0; i < 256; i++) {
  176. if (modifiers_state[i]) {
  177. int qcode = sdl2_scancode_to_qcode[i];
  178. qemu_input_event_send_key_qcode(con, qcode, false);
  179. modifiers_state[i] = 0;
  180. }
  181. }
  182. }
  183. static void sdl_process_key(struct sdl2_state *scon,
  184. SDL_KeyboardEvent *ev)
  185. {
  186. int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode];
  187. QemuConsole *con = scon ? scon->dcl.con : NULL;
  188. if (!qemu_console_is_graphic(con)) {
  189. if (ev->type == SDL_KEYDOWN) {
  190. switch (ev->keysym.scancode) {
  191. case SDL_SCANCODE_RETURN:
  192. kbd_put_keysym_console(con, '\n');
  193. break;
  194. case SDL_SCANCODE_BACKSPACE:
  195. kbd_put_keysym_console(con, QEMU_KEY_BACKSPACE);
  196. break;
  197. default:
  198. kbd_put_qcode_console(con, qcode);
  199. break;
  200. }
  201. }
  202. return;
  203. }
  204. switch (ev->keysym.scancode) {
  205. #if 0
  206. case SDL_SCANCODE_NUMLOCKCLEAR:
  207. case SDL_SCANCODE_CAPSLOCK:
  208. /* SDL does not send the key up event, so we generate it */
  209. qemu_input_event_send_key_qcode(con, qcode, true);
  210. qemu_input_event_send_key_qcode(con, qcode, false);
  211. return;
  212. #endif
  213. case SDL_SCANCODE_LCTRL:
  214. case SDL_SCANCODE_LSHIFT:
  215. case SDL_SCANCODE_LALT:
  216. case SDL_SCANCODE_LGUI:
  217. case SDL_SCANCODE_RCTRL:
  218. case SDL_SCANCODE_RSHIFT:
  219. case SDL_SCANCODE_RALT:
  220. case SDL_SCANCODE_RGUI:
  221. if (ev->type == SDL_KEYUP) {
  222. modifiers_state[ev->keysym.scancode] = 0;
  223. } else {
  224. modifiers_state[ev->keysym.scancode] = 1;
  225. }
  226. /* fall though */
  227. default:
  228. qemu_input_event_send_key_qcode(con, qcode,
  229. ev->type == SDL_KEYDOWN);
  230. }
  231. }
  232. static void sdl_update_caption(struct sdl2_state *scon)
  233. {
  234. char win_title[1024];
  235. char icon_title[1024];
  236. const char *status = "";
  237. if (!runstate_is_running()) {
  238. status = " [Stopped]";
  239. } else if (gui_grab) {
  240. if (alt_grab) {
  241. status = " - Press Ctrl-Alt-Shift to exit mouse grab";
  242. } else if (ctrl_grab) {
  243. status = " - Press Right-Ctrl to exit mouse grab";
  244. } else {
  245. status = " - Press Ctrl-Alt to exit mouse grab";
  246. }
  247. }
  248. if (qemu_name) {
  249. snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name,
  250. scon->idx, status);
  251. snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
  252. } else {
  253. snprintf(win_title, sizeof(win_title), "QEMU%s", status);
  254. snprintf(icon_title, sizeof(icon_title), "QEMU");
  255. }
  256. if (scon->real_window) {
  257. SDL_SetWindowTitle(scon->real_window, win_title);
  258. }
  259. }
  260. static void sdl_hide_cursor(void)
  261. {
  262. if (!cursor_hide) {
  263. return;
  264. }
  265. if (qemu_input_is_absolute()) {
  266. SDL_ShowCursor(1);
  267. SDL_SetCursor(sdl_cursor_hidden);
  268. } else {
  269. SDL_SetRelativeMouseMode(SDL_TRUE);
  270. }
  271. }
  272. static void sdl_show_cursor(void)
  273. {
  274. if (!cursor_hide) {
  275. return;
  276. }
  277. if (!qemu_input_is_absolute()) {
  278. SDL_SetRelativeMouseMode(SDL_FALSE);
  279. SDL_ShowCursor(1);
  280. if (guest_cursor &&
  281. (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
  282. SDL_SetCursor(guest_sprite);
  283. } else {
  284. SDL_SetCursor(sdl_cursor_normal);
  285. }
  286. }
  287. }
  288. static void sdl_grab_start(struct sdl2_state *scon)
  289. {
  290. QemuConsole *con = scon ? scon->dcl.con : NULL;
  291. if (!con || !qemu_console_is_graphic(con)) {
  292. return;
  293. }
  294. /*
  295. * If the application is not active, do not try to enter grab state. This
  296. * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
  297. * application (SDL bug).
  298. */
  299. if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) {
  300. return;
  301. }
  302. if (guest_cursor) {
  303. SDL_SetCursor(guest_sprite);
  304. if (!qemu_input_is_absolute() && !absolute_enabled) {
  305. SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y);
  306. }
  307. } else {
  308. sdl_hide_cursor();
  309. }
  310. SDL_SetWindowGrab(scon->real_window, SDL_TRUE);
  311. gui_grab = 1;
  312. sdl_update_caption(scon);
  313. }
  314. static void sdl_grab_end(struct sdl2_state *scon)
  315. {
  316. SDL_SetWindowGrab(scon->real_window, SDL_FALSE);
  317. gui_grab = 0;
  318. sdl_show_cursor();
  319. sdl_update_caption(scon);
  320. }
  321. static void absolute_mouse_grab(struct sdl2_state *scon)
  322. {
  323. int mouse_x, mouse_y;
  324. int scr_w, scr_h;
  325. SDL_GetMouseState(&mouse_x, &mouse_y);
  326. SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
  327. if (mouse_x > 0 && mouse_x < scr_w - 1 &&
  328. mouse_y > 0 && mouse_y < scr_h - 1) {
  329. sdl_grab_start(scon);
  330. }
  331. }
  332. static void sdl_mouse_mode_change(Notifier *notify, void *data)
  333. {
  334. if (qemu_input_is_absolute()) {
  335. if (!absolute_enabled) {
  336. absolute_enabled = 1;
  337. absolute_mouse_grab(&sdl2_console[0]);
  338. }
  339. } else if (absolute_enabled) {
  340. if (!gui_fullscreen) {
  341. sdl_grab_end(&sdl2_console[0]);
  342. }
  343. absolute_enabled = 0;
  344. }
  345. }
  346. static void sdl_send_mouse_event(struct sdl2_state *scon, int dx, int dy,
  347. int x, int y, int state)
  348. {
  349. static uint32_t bmap[INPUT_BUTTON_MAX] = {
  350. [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT),
  351. [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE),
  352. [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT),
  353. };
  354. static uint32_t prev_state;
  355. if (prev_state != state) {
  356. qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state);
  357. prev_state = state;
  358. }
  359. if (qemu_input_is_absolute()) {
  360. int scr_w, scr_h;
  361. int max_w = 0, max_h = 0;
  362. int off_x = 0, off_y = 0;
  363. int cur_off_x = 0, cur_off_y = 0;
  364. int i;
  365. for (i = 0; i < sdl2_num_outputs; i++) {
  366. struct sdl2_state *thiscon = &sdl2_console[i];
  367. if (thiscon->real_window && thiscon->surface) {
  368. SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h);
  369. cur_off_x = thiscon->x;
  370. cur_off_y = thiscon->y;
  371. if (scr_w + cur_off_x > max_w) {
  372. max_w = scr_w + cur_off_x;
  373. }
  374. if (scr_h + cur_off_y > max_h) {
  375. max_h = scr_h + cur_off_y;
  376. }
  377. if (i == scon->idx) {
  378. off_x = cur_off_x;
  379. off_y = cur_off_y;
  380. }
  381. }
  382. }
  383. qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w);
  384. qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h);
  385. } else {
  386. if (guest_cursor) {
  387. x -= guest_x;
  388. y -= guest_y;
  389. guest_x += x;
  390. guest_y += y;
  391. dx = x;
  392. dy = y;
  393. }
  394. qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, dx);
  395. qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, dy);
  396. }
  397. qemu_input_event_sync();
  398. }
  399. static void sdl_scale(struct sdl2_state *scon, int width, int height)
  400. {
  401. int bpp = 0;
  402. do_sdl_resize(scon, width, height, bpp);
  403. scaling_active = 1;
  404. }
  405. static void toggle_full_screen(struct sdl2_state *scon)
  406. {
  407. int width = surface_width(scon->surface);
  408. int height = surface_height(scon->surface);
  409. int bpp = surface_bits_per_pixel(scon->surface);
  410. gui_fullscreen = !gui_fullscreen;
  411. if (gui_fullscreen) {
  412. SDL_GetWindowSize(scon->real_window,
  413. &gui_saved_width, &gui_saved_height);
  414. gui_saved_scaling = scaling_active;
  415. do_sdl_resize(scon, width, height, bpp);
  416. scaling_active = 0;
  417. gui_saved_grab = gui_grab;
  418. sdl_grab_start(scon);
  419. } else {
  420. if (gui_saved_scaling) {
  421. sdl_scale(scon, gui_saved_width, gui_saved_height);
  422. } else {
  423. do_sdl_resize(scon, width, height, 0);
  424. }
  425. if (!gui_saved_grab) {
  426. sdl_grab_end(scon);
  427. }
  428. }
  429. graphic_hw_invalidate(scon->dcl.con);
  430. graphic_hw_update(scon->dcl.con);
  431. }
  432. static void handle_keydown(SDL_Event *ev)
  433. {
  434. int mod_state, win;
  435. struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
  436. if (alt_grab) {
  437. mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
  438. (gui_grab_code | KMOD_LSHIFT);
  439. } else if (ctrl_grab) {
  440. mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
  441. } else {
  442. mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code;
  443. }
  444. gui_key_modifier_pressed = mod_state;
  445. if (gui_key_modifier_pressed) {
  446. switch (ev->key.keysym.scancode) {
  447. case SDL_SCANCODE_2:
  448. case SDL_SCANCODE_3:
  449. case SDL_SCANCODE_4:
  450. case SDL_SCANCODE_5:
  451. case SDL_SCANCODE_6:
  452. case SDL_SCANCODE_7:
  453. case SDL_SCANCODE_8:
  454. case SDL_SCANCODE_9:
  455. win = ev->key.keysym.scancode - SDL_SCANCODE_1;
  456. if (win < sdl2_num_outputs) {
  457. sdl2_console[win].hidden = !sdl2_console[win].hidden;
  458. if (sdl2_console[win].real_window) {
  459. if (sdl2_console[win].hidden) {
  460. SDL_HideWindow(sdl2_console[win].real_window);
  461. } else {
  462. SDL_ShowWindow(sdl2_console[win].real_window);
  463. }
  464. }
  465. gui_keysym = 1;
  466. }
  467. break;
  468. case SDL_SCANCODE_F:
  469. toggle_full_screen(scon);
  470. gui_keysym = 1;
  471. break;
  472. case SDL_SCANCODE_U:
  473. if (scaling_active) {
  474. scaling_active = 0;
  475. sdl_switch(&scon->dcl, NULL);
  476. graphic_hw_invalidate(scon->dcl.con);
  477. graphic_hw_update(scon->dcl.con);
  478. }
  479. gui_keysym = 1;
  480. break;
  481. case SDL_SCANCODE_KP_PLUS:
  482. case SDL_SCANCODE_KP_MINUS:
  483. if (!gui_fullscreen) {
  484. int scr_w, scr_h;
  485. int width, height;
  486. SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
  487. width = MAX(scr_w + (ev->key.keysym.scancode ==
  488. SDL_SCANCODE_KP_PLUS ? 50 : -50),
  489. 160);
  490. height = (surface_height(scon->surface) * width) /
  491. surface_width(scon->surface);
  492. sdl_scale(scon, width, height);
  493. graphic_hw_invalidate(NULL);
  494. graphic_hw_update(NULL);
  495. gui_keysym = 1;
  496. }
  497. default:
  498. break;
  499. }
  500. }
  501. if (!gui_keysym) {
  502. sdl_process_key(scon, &ev->key);
  503. }
  504. }
  505. static void handle_keyup(SDL_Event *ev)
  506. {
  507. int mod_state;
  508. struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
  509. if (!alt_grab) {
  510. mod_state = (ev->key.keysym.mod & gui_grab_code);
  511. } else {
  512. mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT));
  513. }
  514. if (!mod_state && gui_key_modifier_pressed) {
  515. gui_key_modifier_pressed = 0;
  516. if (gui_keysym == 0) {
  517. /* exit/enter grab if pressing Ctrl-Alt */
  518. if (!gui_grab) {
  519. sdl_grab_start(scon);
  520. } else if (!gui_fullscreen) {
  521. sdl_grab_end(scon);
  522. }
  523. /* SDL does not send back all the modifiers key, so we must
  524. * correct it. */
  525. reset_keys(scon);
  526. return;
  527. }
  528. gui_keysym = 0;
  529. }
  530. if (!gui_keysym) {
  531. sdl_process_key(scon, &ev->key);
  532. }
  533. }
  534. static void handle_textinput(SDL_Event *ev)
  535. {
  536. struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
  537. QemuConsole *con = scon ? scon->dcl.con : NULL;
  538. if (qemu_console_is_graphic(con)) {
  539. return;
  540. }
  541. kbd_put_string_console(con, ev->text.text, strlen(ev->text.text));
  542. }
  543. static void handle_mousemotion(SDL_Event *ev)
  544. {
  545. int max_x, max_y;
  546. struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
  547. if (qemu_input_is_absolute() || absolute_enabled) {
  548. int scr_w, scr_h;
  549. SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
  550. max_x = scr_w - 1;
  551. max_y = scr_h - 1;
  552. if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
  553. ev->motion.x == max_x || ev->motion.y == max_y)) {
  554. sdl_grab_end(scon);
  555. }
  556. if (!gui_grab &&
  557. (ev->motion.x > 0 && ev->motion.x < max_x &&
  558. ev->motion.y > 0 && ev->motion.y < max_y)) {
  559. sdl_grab_start(scon);
  560. }
  561. }
  562. if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
  563. sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel,
  564. ev->motion.x, ev->motion.y, ev->motion.state);
  565. }
  566. }
  567. static void handle_mousebutton(SDL_Event *ev)
  568. {
  569. int buttonstate = SDL_GetMouseState(NULL, NULL);
  570. SDL_MouseButtonEvent *bev;
  571. struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
  572. bev = &ev->button;
  573. if (!gui_grab && !qemu_input_is_absolute()) {
  574. if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
  575. /* start grabbing all events */
  576. sdl_grab_start(scon);
  577. }
  578. } else {
  579. if (ev->type == SDL_MOUSEBUTTONDOWN) {
  580. buttonstate |= SDL_BUTTON(bev->button);
  581. } else {
  582. buttonstate &= ~SDL_BUTTON(bev->button);
  583. }
  584. sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate);
  585. }
  586. }
  587. static void handle_mousewheel(SDL_Event *ev)
  588. {
  589. struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
  590. SDL_MouseWheelEvent *wev = &ev->wheel;
  591. InputButton btn;
  592. if (wev->y > 0) {
  593. btn = INPUT_BUTTON_WHEEL_UP;
  594. } else if (wev->y < 0) {
  595. btn = INPUT_BUTTON_WHEEL_DOWN;
  596. } else {
  597. return;
  598. }
  599. qemu_input_queue_btn(scon->dcl.con, btn, true);
  600. qemu_input_event_sync();
  601. qemu_input_queue_btn(scon->dcl.con, btn, false);
  602. qemu_input_event_sync();
  603. }
  604. static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev)
  605. {
  606. int w, h;
  607. struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
  608. switch (ev->window.event) {
  609. case SDL_WINDOWEVENT_RESIZED:
  610. sdl_scale(scon, ev->window.data1, ev->window.data2);
  611. {
  612. QemuUIInfo info;
  613. memset(&info, 0, sizeof(info));
  614. info.width = ev->window.data1;
  615. info.height = ev->window.data2;
  616. dpy_set_ui_info(scon->dcl.con, &info);
  617. }
  618. graphic_hw_invalidate(scon->dcl.con);
  619. graphic_hw_update(scon->dcl.con);
  620. break;
  621. case SDL_WINDOWEVENT_EXPOSED:
  622. SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h);
  623. sdl_update(dcl, 0, 0, w, h);
  624. break;
  625. case SDL_WINDOWEVENT_FOCUS_GAINED:
  626. case SDL_WINDOWEVENT_ENTER:
  627. if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) {
  628. absolute_mouse_grab(scon);
  629. }
  630. break;
  631. case SDL_WINDOWEVENT_FOCUS_LOST:
  632. if (gui_grab && !gui_fullscreen) {
  633. sdl_grab_end(scon);
  634. }
  635. break;
  636. case SDL_WINDOWEVENT_RESTORED:
  637. update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT);
  638. break;
  639. case SDL_WINDOWEVENT_MINIMIZED:
  640. update_displaychangelistener(dcl, 500);
  641. break;
  642. case SDL_WINDOWEVENT_CLOSE:
  643. if (!no_quit) {
  644. no_shutdown = 0;
  645. qemu_system_shutdown_request();
  646. }
  647. break;
  648. }
  649. }
  650. static void sdl_refresh(DisplayChangeListener *dcl)
  651. {
  652. struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
  653. SDL_Event ev1, *ev = &ev1;
  654. if (scon->last_vm_running != runstate_is_running()) {
  655. scon->last_vm_running = runstate_is_running();
  656. sdl_update_caption(scon);
  657. }
  658. graphic_hw_update(dcl->con);
  659. while (SDL_PollEvent(ev)) {
  660. switch (ev->type) {
  661. case SDL_KEYDOWN:
  662. handle_keydown(ev);
  663. break;
  664. case SDL_KEYUP:
  665. handle_keyup(ev);
  666. break;
  667. case SDL_TEXTINPUT:
  668. handle_textinput(ev);
  669. break;
  670. case SDL_QUIT:
  671. if (!no_quit) {
  672. no_shutdown = 0;
  673. qemu_system_shutdown_request();
  674. }
  675. break;
  676. case SDL_MOUSEMOTION:
  677. handle_mousemotion(ev);
  678. break;
  679. case SDL_MOUSEBUTTONDOWN:
  680. case SDL_MOUSEBUTTONUP:
  681. handle_mousebutton(ev);
  682. break;
  683. case SDL_MOUSEWHEEL:
  684. handle_mousewheel(ev);
  685. break;
  686. case SDL_WINDOWEVENT:
  687. handle_windowevent(dcl, ev);
  688. break;
  689. default:
  690. break;
  691. }
  692. }
  693. }
  694. static void sdl_mouse_warp(DisplayChangeListener *dcl,
  695. int x, int y, int on)
  696. {
  697. struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
  698. if (on) {
  699. if (!guest_cursor) {
  700. sdl_show_cursor();
  701. }
  702. if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
  703. SDL_SetCursor(guest_sprite);
  704. if (!qemu_input_is_absolute() && !absolute_enabled) {
  705. SDL_WarpMouseInWindow(scon->real_window, x, y);
  706. }
  707. }
  708. } else if (gui_grab) {
  709. sdl_hide_cursor();
  710. }
  711. guest_cursor = on;
  712. guest_x = x, guest_y = y;
  713. }
  714. static void sdl_mouse_define(DisplayChangeListener *dcl,
  715. QEMUCursor *c)
  716. {
  717. if (guest_sprite) {
  718. SDL_FreeCursor(guest_sprite);
  719. }
  720. if (guest_sprite_surface) {
  721. SDL_FreeSurface(guest_sprite_surface);
  722. }
  723. guest_sprite_surface =
  724. SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4,
  725. 0xff0000, 0x00ff00, 0xff, 0xff000000);
  726. if (!guest_sprite_surface) {
  727. fprintf(stderr, "Failed to make rgb surface from %p\n", c);
  728. return;
  729. }
  730. guest_sprite = SDL_CreateColorCursor(guest_sprite_surface,
  731. c->hot_x, c->hot_y);
  732. if (!guest_sprite) {
  733. fprintf(stderr, "Failed to make color cursor from %p\n", c);
  734. return;
  735. }
  736. if (guest_cursor &&
  737. (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
  738. SDL_SetCursor(guest_sprite);
  739. }
  740. }
  741. static void sdl_cleanup(void)
  742. {
  743. if (guest_sprite) {
  744. SDL_FreeCursor(guest_sprite);
  745. }
  746. SDL_QuitSubSystem(SDL_INIT_VIDEO);
  747. }
  748. static const DisplayChangeListenerOps dcl_ops = {
  749. .dpy_name = "sdl",
  750. .dpy_gfx_update = sdl_update,
  751. .dpy_gfx_switch = sdl_switch,
  752. .dpy_refresh = sdl_refresh,
  753. .dpy_mouse_set = sdl_mouse_warp,
  754. .dpy_cursor_define = sdl_mouse_define,
  755. };
  756. void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
  757. {
  758. int flags;
  759. uint8_t data = 0;
  760. char *filename;
  761. int i;
  762. if (no_frame) {
  763. gui_noframe = 1;
  764. }
  765. #ifdef __linux__
  766. /* on Linux, SDL may use fbcon|directfb|svgalib when run without
  767. * accessible $DISPLAY to open X11 window. This is often the case
  768. * when qemu is run using sudo. But in this case, and when actually
  769. * run in X11 environment, SDL fights with X11 for the video card,
  770. * making current display unavailable, often until reboot.
  771. * So make x11 the default SDL video driver if this variable is unset.
  772. * This is a bit hackish but saves us from bigger problem.
  773. * Maybe it's a good idea to fix this in SDL instead.
  774. */
  775. setenv("SDL_VIDEODRIVER", "x11", 0);
  776. #endif
  777. flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
  778. if (SDL_Init(flags)) {
  779. fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
  780. SDL_GetError());
  781. exit(1);
  782. }
  783. for (i = 0;; i++) {
  784. QemuConsole *con = qemu_console_lookup_by_index(i);
  785. if (!con) {
  786. break;
  787. }
  788. }
  789. sdl2_num_outputs = i;
  790. sdl2_console = g_new0(struct sdl2_state, sdl2_num_outputs);
  791. for (i = 0; i < sdl2_num_outputs; i++) {
  792. QemuConsole *con = qemu_console_lookup_by_index(i);
  793. if (!qemu_console_is_graphic(con)) {
  794. sdl2_console[i].hidden = true;
  795. }
  796. sdl2_console[i].dcl.ops = &dcl_ops;
  797. sdl2_console[i].dcl.con = con;
  798. register_displaychangelistener(&sdl2_console[i].dcl);
  799. sdl2_console[i].idx = i;
  800. }
  801. /* Load a 32x32x4 image. White pixels are transparent. */
  802. filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
  803. if (filename) {
  804. SDL_Surface *image = SDL_LoadBMP(filename);
  805. if (image) {
  806. uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
  807. SDL_SetColorKey(image, SDL_TRUE, colorkey);
  808. SDL_SetWindowIcon(sdl2_console[0].real_window, image);
  809. }
  810. g_free(filename);
  811. }
  812. if (full_screen) {
  813. gui_fullscreen = 1;
  814. sdl_grab_start(0);
  815. }
  816. mouse_mode_notifier.notify = sdl_mouse_mode_change;
  817. qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
  818. gui_grab = 0;
  819. sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
  820. sdl_cursor_normal = SDL_GetCursor();
  821. atexit(sdl_cleanup);
  822. }
  823. #endif