sdl2.c 25 KB

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