2
0

gtk.c 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941
  1. /*
  2. * GTK UI
  3. *
  4. * Copyright IBM, Corp. 2012
  5. *
  6. * Authors:
  7. * Anthony Liguori <aliguori@us.ibm.com>
  8. *
  9. * This work is licensed under the terms of the GNU GPL, version 2 or later.
  10. * See the COPYING file in the top-level directory.
  11. *
  12. * Portions from gtk-vnc:
  13. *
  14. * GTK VNC Widget
  15. *
  16. * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
  17. * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
  18. *
  19. * This library is free software; you can redistribute it and/or
  20. * modify it under the terms of the GNU Lesser General Public
  21. * License as published by the Free Software Foundation; either
  22. * version 2.0 of the License, or (at your option) any later version.
  23. *
  24. * This library is distributed in the hope that it will be useful,
  25. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  27. * Lesser General Public License for more details.
  28. *
  29. * You should have received a copy of the GNU Lesser General Public
  30. * License along with this library; if not, write to the Free Software
  31. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  32. */
  33. #define GETTEXT_PACKAGE "qemu"
  34. #define LOCALEDIR "po"
  35. #ifdef _WIN32
  36. # define _WIN32_WINNT 0x0601 /* needed to get definition of MAPVK_VK_TO_VSC */
  37. #endif
  38. #include "qemu-common.h"
  39. #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
  40. /* Work around an -Wstrict-prototypes warning in GTK headers */
  41. #pragma GCC diagnostic push
  42. #pragma GCC diagnostic ignored "-Wstrict-prototypes"
  43. #endif
  44. #include <gtk/gtk.h>
  45. #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
  46. #pragma GCC diagnostic pop
  47. #endif
  48. #include <gdk/gdkkeysyms.h>
  49. #include <glib/gi18n.h>
  50. #include <locale.h>
  51. #if defined(CONFIG_VTE)
  52. #include <vte/vte.h>
  53. #endif
  54. #include <math.h>
  55. #include "trace.h"
  56. #include "ui/console.h"
  57. #include "ui/input.h"
  58. #include "sysemu/sysemu.h"
  59. #include "qmp-commands.h"
  60. #include "x_keymap.h"
  61. #include "keymaps.h"
  62. #include "sysemu/char.h"
  63. #include "qom/object.h"
  64. #ifdef GDK_WINDOWING_X11
  65. #include <gdk/gdkx.h>
  66. #include <X11/XKBlib.h>
  67. #endif
  68. #define MAX_VCS 10
  69. #define VC_WINDOW_X_MIN 320
  70. #define VC_WINDOW_Y_MIN 240
  71. #define VC_TERM_X_MIN 80
  72. #define VC_TERM_Y_MIN 25
  73. #define VC_SCALE_MIN 0.25
  74. #define VC_SCALE_STEP 0.25
  75. #if !defined(CONFIG_VTE)
  76. # define VTE_CHECK_VERSION(a, b, c) 0
  77. #endif
  78. #if defined(CONFIG_VTE) && !GTK_CHECK_VERSION(3, 0, 0)
  79. /*
  80. * The gtk2 vte terminal widget seriously messes up the window resize
  81. * for some reason. You basically can't make the qemu window smaller
  82. * any more because the toplevel window geoemtry hints are overridden.
  83. *
  84. * Workaround that by hiding all vte widgets, except the one in the
  85. * current tab.
  86. *
  87. * Luckily everything works smooth in gtk3.
  88. */
  89. # define VTE_RESIZE_HACK 1
  90. #endif
  91. /* Compatibility define to let us build on both Gtk2 and Gtk3 */
  92. #if GTK_CHECK_VERSION(3, 0, 0)
  93. static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh)
  94. {
  95. *ww = gdk_window_get_width(w);
  96. *wh = gdk_window_get_height(w);
  97. }
  98. #endif
  99. #if !GTK_CHECK_VERSION(2, 20, 0)
  100. #define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget)
  101. #endif
  102. #ifndef GDK_IS_X11_DISPLAY
  103. #define GDK_IS_X11_DISPLAY(dpy) (dpy == dpy)
  104. #endif
  105. #ifndef GDK_IS_WIN32_DISPLAY
  106. #define GDK_IS_WIN32_DISPLAY(dpy) (dpy == dpy)
  107. #endif
  108. #ifndef GDK_KEY_0
  109. #define GDK_KEY_0 GDK_0
  110. #define GDK_KEY_1 GDK_1
  111. #define GDK_KEY_2 GDK_2
  112. #define GDK_KEY_f GDK_f
  113. #define GDK_KEY_g GDK_g
  114. #define GDK_KEY_q GDK_q
  115. #define GDK_KEY_plus GDK_plus
  116. #define GDK_KEY_minus GDK_minus
  117. #define GDK_KEY_Pause GDK_Pause
  118. #endif
  119. #define HOTKEY_MODIFIERS (GDK_CONTROL_MASK | GDK_MOD1_MASK)
  120. static const int modifier_keycode[] = {
  121. /* shift, control, alt keys, meta keys, both left & right */
  122. 0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd,
  123. };
  124. typedef struct GtkDisplayState GtkDisplayState;
  125. typedef struct VirtualGfxConsole {
  126. GtkWidget *drawing_area;
  127. DisplayChangeListener dcl;
  128. DisplaySurface *ds;
  129. pixman_image_t *convert;
  130. cairo_surface_t *surface;
  131. double scale_x;
  132. double scale_y;
  133. } VirtualGfxConsole;
  134. #if defined(CONFIG_VTE)
  135. typedef struct VirtualVteConsole {
  136. GtkWidget *box;
  137. GtkWidget *scrollbar;
  138. GtkWidget *terminal;
  139. CharDriverState *chr;
  140. } VirtualVteConsole;
  141. #endif
  142. typedef enum VirtualConsoleType {
  143. GD_VC_GFX,
  144. GD_VC_VTE,
  145. } VirtualConsoleType;
  146. typedef struct VirtualConsole {
  147. GtkDisplayState *s;
  148. char *label;
  149. GtkWidget *window;
  150. GtkWidget *menu_item;
  151. GtkWidget *tab_item;
  152. VirtualConsoleType type;
  153. union {
  154. VirtualGfxConsole gfx;
  155. #if defined(CONFIG_VTE)
  156. VirtualVteConsole vte;
  157. #endif
  158. };
  159. } VirtualConsole;
  160. struct GtkDisplayState {
  161. GtkWidget *window;
  162. GtkWidget *menu_bar;
  163. GtkAccelGroup *accel_group;
  164. GtkWidget *machine_menu_item;
  165. GtkWidget *machine_menu;
  166. GtkWidget *pause_item;
  167. GtkWidget *reset_item;
  168. GtkWidget *powerdown_item;
  169. GtkWidget *quit_item;
  170. GtkWidget *view_menu_item;
  171. GtkWidget *view_menu;
  172. GtkWidget *full_screen_item;
  173. GtkWidget *zoom_in_item;
  174. GtkWidget *zoom_out_item;
  175. GtkWidget *zoom_fixed_item;
  176. GtkWidget *zoom_fit_item;
  177. GtkWidget *grab_item;
  178. GtkWidget *grab_on_hover_item;
  179. int nb_vcs;
  180. VirtualConsole vc[MAX_VCS];
  181. GtkWidget *show_tabs_item;
  182. GtkWidget *untabify_item;
  183. GtkWidget *vbox;
  184. GtkWidget *notebook;
  185. int button_mask;
  186. gboolean last_set;
  187. int last_x;
  188. int last_y;
  189. int grab_x_root;
  190. int grab_y_root;
  191. VirtualConsole *kbd_owner;
  192. VirtualConsole *ptr_owner;
  193. gboolean full_screen;
  194. GdkCursor *null_cursor;
  195. Notifier mouse_mode_notifier;
  196. gboolean free_scale;
  197. bool external_pause_update;
  198. bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
  199. bool has_evdev;
  200. };
  201. static void gd_grab_pointer(VirtualConsole *vc);
  202. static void gd_ungrab_pointer(GtkDisplayState *s);
  203. /** Utility Functions **/
  204. static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s)
  205. {
  206. VirtualConsole *vc;
  207. gint i;
  208. for (i = 0; i < s->nb_vcs; i++) {
  209. vc = &s->vc[i];
  210. if (gtk_check_menu_item_get_active
  211. (GTK_CHECK_MENU_ITEM(vc->menu_item))) {
  212. return vc;
  213. }
  214. }
  215. return NULL;
  216. }
  217. static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page)
  218. {
  219. VirtualConsole *vc;
  220. gint i, p;
  221. for (i = 0; i < s->nb_vcs; i++) {
  222. vc = &s->vc[i];
  223. p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item);
  224. if (p == page) {
  225. return vc;
  226. }
  227. }
  228. return NULL;
  229. }
  230. static VirtualConsole *gd_vc_find_current(GtkDisplayState *s)
  231. {
  232. gint page;
  233. page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook));
  234. return gd_vc_find_by_page(s, page);
  235. }
  236. static bool gd_is_grab_active(GtkDisplayState *s)
  237. {
  238. return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
  239. }
  240. static bool gd_grab_on_hover(GtkDisplayState *s)
  241. {
  242. return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
  243. }
  244. static void gd_update_cursor(VirtualConsole *vc)
  245. {
  246. GtkDisplayState *s = vc->s;
  247. GdkWindow *window;
  248. if (vc->type != GD_VC_GFX) {
  249. return;
  250. }
  251. window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area));
  252. if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) {
  253. gdk_window_set_cursor(window, s->null_cursor);
  254. } else {
  255. gdk_window_set_cursor(window, NULL);
  256. }
  257. }
  258. static void gd_update_caption(GtkDisplayState *s)
  259. {
  260. const char *status = "";
  261. gchar *prefix;
  262. gchar *title;
  263. const char *grab = "";
  264. bool is_paused = !runstate_is_running();
  265. int i;
  266. if (qemu_name) {
  267. prefix = g_strdup_printf("QEMU (%s)", qemu_name);
  268. } else {
  269. prefix = g_strdup_printf("QEMU");
  270. }
  271. if (s->ptr_owner != NULL &&
  272. s->ptr_owner->window == NULL) {
  273. grab = _(" - Press Ctrl+Alt+G to release grab");
  274. }
  275. if (is_paused) {
  276. status = _(" [Paused]");
  277. }
  278. s->external_pause_update = true;
  279. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item),
  280. is_paused);
  281. s->external_pause_update = false;
  282. title = g_strdup_printf("%s%s%s", prefix, status, grab);
  283. gtk_window_set_title(GTK_WINDOW(s->window), title);
  284. g_free(title);
  285. for (i = 0; i < s->nb_vcs; i++) {
  286. VirtualConsole *vc = &s->vc[i];
  287. if (!vc->window) {
  288. continue;
  289. }
  290. title = g_strdup_printf("%s: %s%s%s", prefix, vc->label,
  291. vc == s->kbd_owner ? " +kbd" : "",
  292. vc == s->ptr_owner ? " +ptr" : "");
  293. gtk_window_set_title(GTK_WINDOW(vc->window), title);
  294. g_free(title);
  295. }
  296. g_free(prefix);
  297. }
  298. static void gd_update_geometry_hints(VirtualConsole *vc)
  299. {
  300. GtkDisplayState *s = vc->s;
  301. GdkWindowHints mask = 0;
  302. GdkGeometry geo = {};
  303. GtkWidget *geo_widget = NULL;
  304. GtkWindow *geo_window;
  305. if (vc->type == GD_VC_GFX) {
  306. if (s->free_scale) {
  307. geo.min_width = surface_width(vc->gfx.ds) * VC_SCALE_MIN;
  308. geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN;
  309. mask |= GDK_HINT_MIN_SIZE;
  310. } else {
  311. geo.min_width = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
  312. geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
  313. mask |= GDK_HINT_MIN_SIZE;
  314. }
  315. geo_widget = vc->gfx.drawing_area;
  316. gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height);
  317. #if defined(CONFIG_VTE)
  318. } else if (vc->type == GD_VC_VTE) {
  319. VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
  320. GtkBorder *ib;
  321. geo.width_inc = vte_terminal_get_char_width(term);
  322. geo.height_inc = vte_terminal_get_char_height(term);
  323. mask |= GDK_HINT_RESIZE_INC;
  324. geo.base_width = geo.width_inc;
  325. geo.base_height = geo.height_inc;
  326. mask |= GDK_HINT_BASE_SIZE;
  327. geo.min_width = geo.width_inc * VC_TERM_X_MIN;
  328. geo.min_height = geo.height_inc * VC_TERM_Y_MIN;
  329. mask |= GDK_HINT_MIN_SIZE;
  330. gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL);
  331. geo.base_width += ib->left + ib->right;
  332. geo.base_height += ib->top + ib->bottom;
  333. geo.min_width += ib->left + ib->right;
  334. geo.min_height += ib->top + ib->bottom;
  335. geo_widget = vc->vte.terminal;
  336. #endif
  337. }
  338. geo_window = GTK_WINDOW(vc->window ? vc->window : s->window);
  339. gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask);
  340. }
  341. static void gd_update_windowsize(VirtualConsole *vc)
  342. {
  343. GtkDisplayState *s = vc->s;
  344. gd_update_geometry_hints(vc);
  345. if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) {
  346. gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window),
  347. VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN);
  348. }
  349. }
  350. static void gd_update_full_redraw(VirtualConsole *vc)
  351. {
  352. GtkWidget *area = vc->gfx.drawing_area;
  353. int ww, wh;
  354. gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
  355. gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
  356. }
  357. static void gtk_release_modifiers(GtkDisplayState *s)
  358. {
  359. VirtualConsole *vc = gd_vc_find_current(s);
  360. int i, keycode;
  361. if (vc->type != GD_VC_GFX) {
  362. return;
  363. }
  364. for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
  365. keycode = modifier_keycode[i];
  366. if (!s->modifier_pressed[i]) {
  367. continue;
  368. }
  369. qemu_input_event_send_key_number(vc->gfx.dcl.con, keycode, false);
  370. s->modifier_pressed[i] = false;
  371. }
  372. }
  373. static void gd_widget_reparent(GtkWidget *from, GtkWidget *to,
  374. GtkWidget *widget)
  375. {
  376. g_object_ref(G_OBJECT(widget));
  377. gtk_container_remove(GTK_CONTAINER(from), widget);
  378. gtk_container_add(GTK_CONTAINER(to), widget);
  379. g_object_unref(G_OBJECT(widget));
  380. }
  381. /** DisplayState Callbacks **/
  382. static void gd_update(DisplayChangeListener *dcl,
  383. int x, int y, int w, int h)
  384. {
  385. VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
  386. int x1, x2, y1, y2;
  387. int mx, my;
  388. int fbw, fbh;
  389. int ww, wh;
  390. trace_gd_update(vc->label, x, y, w, h);
  391. if (vc->gfx.convert) {
  392. pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
  393. NULL, vc->gfx.convert,
  394. x, y, 0, 0, x, y, w, h);
  395. }
  396. x1 = floor(x * vc->gfx.scale_x);
  397. y1 = floor(y * vc->gfx.scale_y);
  398. x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x);
  399. y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y);
  400. fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
  401. fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
  402. gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area),
  403. &ww, &wh);
  404. mx = my = 0;
  405. if (ww > fbw) {
  406. mx = (ww - fbw) / 2;
  407. }
  408. if (wh > fbh) {
  409. my = (wh - fbh) / 2;
  410. }
  411. gtk_widget_queue_draw_area(vc->gfx.drawing_area,
  412. mx + x1, my + y1, (x2 - x1), (y2 - y1));
  413. }
  414. static void gd_refresh(DisplayChangeListener *dcl)
  415. {
  416. graphic_hw_update(dcl->con);
  417. }
  418. #if GTK_CHECK_VERSION(3, 0, 0)
  419. static void gd_mouse_set(DisplayChangeListener *dcl,
  420. int x, int y, int visible)
  421. {
  422. VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
  423. GdkDisplay *dpy;
  424. GdkDeviceManager *mgr;
  425. gint x_root, y_root;
  426. if (qemu_input_is_absolute()) {
  427. return;
  428. }
  429. dpy = gtk_widget_get_display(vc->gfx.drawing_area);
  430. mgr = gdk_display_get_device_manager(dpy);
  431. gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
  432. x, y, &x_root, &y_root);
  433. gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
  434. gtk_widget_get_screen(vc->gfx.drawing_area),
  435. x_root, y_root);
  436. }
  437. #else
  438. static void gd_mouse_set(DisplayChangeListener *dcl,
  439. int x, int y, int visible)
  440. {
  441. VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
  442. gint x_root, y_root;
  443. if (qemu_input_is_absolute()) {
  444. return;
  445. }
  446. gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
  447. x, y, &x_root, &y_root);
  448. gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area),
  449. gtk_widget_get_screen(vc->gfx.drawing_area),
  450. x_root, y_root);
  451. }
  452. #endif
  453. static void gd_cursor_define(DisplayChangeListener *dcl,
  454. QEMUCursor *c)
  455. {
  456. VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
  457. GdkPixbuf *pixbuf;
  458. GdkCursor *cursor;
  459. pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data),
  460. GDK_COLORSPACE_RGB, true, 8,
  461. c->width, c->height, c->width * 4,
  462. NULL, NULL);
  463. cursor = gdk_cursor_new_from_pixbuf
  464. (gtk_widget_get_display(vc->gfx.drawing_area),
  465. pixbuf, c->hot_x, c->hot_y);
  466. gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor);
  467. g_object_unref(pixbuf);
  468. #if !GTK_CHECK_VERSION(3, 0, 0)
  469. gdk_cursor_unref(cursor);
  470. #else
  471. g_object_unref(cursor);
  472. #endif
  473. }
  474. static void gd_switch(DisplayChangeListener *dcl,
  475. DisplaySurface *surface)
  476. {
  477. VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
  478. bool resized = true;
  479. trace_gd_switch(vc->label, surface_width(surface), surface_height(surface));
  480. if (vc->gfx.surface) {
  481. cairo_surface_destroy(vc->gfx.surface);
  482. }
  483. if (vc->gfx.ds &&
  484. surface_width(vc->gfx.ds) == surface_width(surface) &&
  485. surface_height(vc->gfx.ds) == surface_height(surface)) {
  486. resized = false;
  487. }
  488. vc->gfx.ds = surface;
  489. if (vc->gfx.convert) {
  490. pixman_image_unref(vc->gfx.convert);
  491. vc->gfx.convert = NULL;
  492. }
  493. if (surface->format == PIXMAN_x8r8g8b8) {
  494. /*
  495. * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24
  496. *
  497. * No need to convert, use surface directly. Should be the
  498. * common case as this is qemu_default_pixelformat(32) too.
  499. */
  500. vc->gfx.surface = cairo_image_surface_create_for_data
  501. (surface_data(surface),
  502. CAIRO_FORMAT_RGB24,
  503. surface_width(surface),
  504. surface_height(surface),
  505. surface_stride(surface));
  506. } else {
  507. /* Must convert surface, use pixman to do it. */
  508. vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8,
  509. surface_width(surface),
  510. surface_height(surface),
  511. NULL, 0);
  512. vc->gfx.surface = cairo_image_surface_create_for_data
  513. ((void *)pixman_image_get_data(vc->gfx.convert),
  514. CAIRO_FORMAT_RGB24,
  515. pixman_image_get_width(vc->gfx.convert),
  516. pixman_image_get_height(vc->gfx.convert),
  517. pixman_image_get_stride(vc->gfx.convert));
  518. pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
  519. NULL, vc->gfx.convert,
  520. 0, 0, 0, 0, 0, 0,
  521. pixman_image_get_width(vc->gfx.convert),
  522. pixman_image_get_height(vc->gfx.convert));
  523. }
  524. if (resized) {
  525. gd_update_windowsize(vc);
  526. } else {
  527. gd_update_full_redraw(vc);
  528. }
  529. }
  530. /** QEMU Events **/
  531. static void gd_change_runstate(void *opaque, int running, RunState state)
  532. {
  533. GtkDisplayState *s = opaque;
  534. gd_update_caption(s);
  535. }
  536. static void gd_mouse_mode_change(Notifier *notify, void *data)
  537. {
  538. GtkDisplayState *s;
  539. int i;
  540. s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
  541. /* release the grab at switching to absolute mode */
  542. if (qemu_input_is_absolute() && gd_is_grab_active(s)) {
  543. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
  544. FALSE);
  545. }
  546. for (i = 0; i < s->nb_vcs; i++) {
  547. VirtualConsole *vc = &s->vc[i];
  548. gd_update_cursor(vc);
  549. }
  550. }
  551. /** GTK Events **/
  552. static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
  553. void *opaque)
  554. {
  555. GtkDisplayState *s = opaque;
  556. int i;
  557. if (!no_quit) {
  558. for (i = 0; i < s->nb_vcs; i++) {
  559. if (s->vc[i].type != GD_VC_GFX) {
  560. continue;
  561. }
  562. unregister_displaychangelistener(&s->vc[i].gfx.dcl);
  563. }
  564. qmp_quit(NULL);
  565. return FALSE;
  566. }
  567. return TRUE;
  568. }
  569. static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
  570. {
  571. VirtualConsole *vc = opaque;
  572. GtkDisplayState *s = vc->s;
  573. int mx, my;
  574. int ww, wh;
  575. int fbw, fbh;
  576. if (!gtk_widget_get_realized(widget)) {
  577. return FALSE;
  578. }
  579. fbw = surface_width(vc->gfx.ds);
  580. fbh = surface_height(vc->gfx.ds);
  581. gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
  582. if (s->full_screen) {
  583. vc->gfx.scale_x = (double)ww / fbw;
  584. vc->gfx.scale_y = (double)wh / fbh;
  585. } else if (s->free_scale) {
  586. double sx, sy;
  587. sx = (double)ww / fbw;
  588. sy = (double)wh / fbh;
  589. vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
  590. }
  591. fbw *= vc->gfx.scale_x;
  592. fbh *= vc->gfx.scale_y;
  593. mx = my = 0;
  594. if (ww > fbw) {
  595. mx = (ww - fbw) / 2;
  596. }
  597. if (wh > fbh) {
  598. my = (wh - fbh) / 2;
  599. }
  600. cairo_rectangle(cr, 0, 0, ww, wh);
  601. /* Optionally cut out the inner area where the pixmap
  602. will be drawn. This avoids 'flashing' since we're
  603. not double-buffering. Note we're using the undocumented
  604. behaviour of drawing the rectangle from right to left
  605. to cut out the whole */
  606. cairo_rectangle(cr, mx + fbw, my,
  607. -1 * fbw, fbh);
  608. cairo_fill(cr);
  609. cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y);
  610. cairo_set_source_surface(cr, vc->gfx.surface,
  611. mx / vc->gfx.scale_x, my / vc->gfx.scale_y);
  612. cairo_paint(cr);
  613. return TRUE;
  614. }
  615. #if !GTK_CHECK_VERSION(3, 0, 0)
  616. static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
  617. void *opaque)
  618. {
  619. cairo_t *cr;
  620. gboolean ret;
  621. cr = gdk_cairo_create(gtk_widget_get_window(widget));
  622. cairo_rectangle(cr,
  623. expose->area.x,
  624. expose->area.y,
  625. expose->area.width,
  626. expose->area.height);
  627. cairo_clip(cr);
  628. ret = gd_draw_event(widget, cr, opaque);
  629. cairo_destroy(cr);
  630. return ret;
  631. }
  632. #endif
  633. static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
  634. void *opaque)
  635. {
  636. VirtualConsole *vc = opaque;
  637. GtkDisplayState *s = vc->s;
  638. int x, y;
  639. int mx, my;
  640. int fbh, fbw;
  641. int ww, wh;
  642. fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
  643. fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
  644. gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area),
  645. &ww, &wh);
  646. mx = my = 0;
  647. if (ww > fbw) {
  648. mx = (ww - fbw) / 2;
  649. }
  650. if (wh > fbh) {
  651. my = (wh - fbh) / 2;
  652. }
  653. x = (motion->x - mx) / vc->gfx.scale_x;
  654. y = (motion->y - my) / vc->gfx.scale_y;
  655. if (qemu_input_is_absolute()) {
  656. if (x < 0 || y < 0 ||
  657. x >= surface_width(vc->gfx.ds) ||
  658. y >= surface_height(vc->gfx.ds)) {
  659. return TRUE;
  660. }
  661. qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
  662. surface_width(vc->gfx.ds));
  663. qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
  664. surface_height(vc->gfx.ds));
  665. qemu_input_event_sync();
  666. } else if (s->last_set && s->ptr_owner == vc) {
  667. qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
  668. qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
  669. qemu_input_event_sync();
  670. }
  671. s->last_x = x;
  672. s->last_y = y;
  673. s->last_set = TRUE;
  674. if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
  675. GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
  676. int x = (int)motion->x_root;
  677. int y = (int)motion->y_root;
  678. /* In relative mode check to see if client pointer hit
  679. * one of the screen edges, and if so move it back by
  680. * 200 pixels. This is important because the pointer
  681. * in the server doesn't correspond 1-for-1, and so
  682. * may still be only half way across the screen. Without
  683. * this warp, the server pointer would thus appear to hit
  684. * an invisible wall */
  685. if (x == 0) {
  686. x += 200;
  687. }
  688. if (y == 0) {
  689. y += 200;
  690. }
  691. if (x == (gdk_screen_get_width(screen) - 1)) {
  692. x -= 200;
  693. }
  694. if (y == (gdk_screen_get_height(screen) - 1)) {
  695. y -= 200;
  696. }
  697. if (x != (int)motion->x_root || y != (int)motion->y_root) {
  698. #if GTK_CHECK_VERSION(3, 0, 0)
  699. GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
  700. gdk_device_warp(dev, screen, x, y);
  701. #else
  702. GdkDisplay *display = gtk_widget_get_display(widget);
  703. gdk_display_warp_pointer(display, screen, x, y);
  704. #endif
  705. s->last_set = FALSE;
  706. return FALSE;
  707. }
  708. }
  709. return TRUE;
  710. }
  711. static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
  712. void *opaque)
  713. {
  714. VirtualConsole *vc = opaque;
  715. GtkDisplayState *s = vc->s;
  716. InputButton btn;
  717. /* implicitly grab the input at the first click in the relative mode */
  718. if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
  719. !qemu_input_is_absolute() && s->ptr_owner != vc) {
  720. gd_ungrab_pointer(s);
  721. if (!vc->window) {
  722. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
  723. TRUE);
  724. } else {
  725. gd_grab_pointer(vc);
  726. gd_update_caption(s);
  727. }
  728. return TRUE;
  729. }
  730. if (button->button == 1) {
  731. btn = INPUT_BUTTON_LEFT;
  732. } else if (button->button == 2) {
  733. btn = INPUT_BUTTON_MIDDLE;
  734. } else if (button->button == 3) {
  735. btn = INPUT_BUTTON_RIGHT;
  736. } else {
  737. return TRUE;
  738. }
  739. qemu_input_queue_btn(vc->gfx.dcl.con, btn,
  740. button->type == GDK_BUTTON_PRESS);
  741. qemu_input_event_sync();
  742. return TRUE;
  743. }
  744. static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
  745. void *opaque)
  746. {
  747. VirtualConsole *vc = opaque;
  748. InputButton btn;
  749. if (scroll->direction == GDK_SCROLL_UP) {
  750. btn = INPUT_BUTTON_WHEEL_UP;
  751. } else if (scroll->direction == GDK_SCROLL_DOWN) {
  752. btn = INPUT_BUTTON_WHEEL_DOWN;
  753. } else {
  754. return TRUE;
  755. }
  756. qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
  757. qemu_input_event_sync();
  758. qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
  759. qemu_input_event_sync();
  760. return TRUE;
  761. }
  762. static int gd_map_keycode(GtkDisplayState *s, GdkDisplay *dpy, int gdk_keycode)
  763. {
  764. int qemu_keycode;
  765. #ifdef GDK_WINDOWING_WIN32
  766. if (GDK_IS_WIN32_DISPLAY(dpy)) {
  767. qemu_keycode = MapVirtualKey(gdk_keycode, MAPVK_VK_TO_VSC);
  768. switch (qemu_keycode) {
  769. case 103: /* alt gr */
  770. qemu_keycode = 56 | SCANCODE_GREY;
  771. break;
  772. }
  773. return qemu_keycode;
  774. }
  775. #endif
  776. if (gdk_keycode < 9) {
  777. qemu_keycode = 0;
  778. } else if (gdk_keycode < 97) {
  779. qemu_keycode = gdk_keycode - 8;
  780. #ifdef GDK_WINDOWING_X11
  781. } else if (GDK_IS_X11_DISPLAY(dpy) && gdk_keycode < 158) {
  782. if (s->has_evdev) {
  783. qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
  784. } else {
  785. qemu_keycode = translate_xfree86_keycode(gdk_keycode - 97);
  786. }
  787. #endif
  788. } else if (gdk_keycode == 208) { /* Hiragana_Katakana */
  789. qemu_keycode = 0x70;
  790. } else if (gdk_keycode == 211) { /* backslash */
  791. qemu_keycode = 0x73;
  792. } else {
  793. qemu_keycode = 0;
  794. }
  795. return qemu_keycode;
  796. }
  797. static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
  798. {
  799. VirtualConsole *vc = opaque;
  800. GtkDisplayState *s = vc->s;
  801. int gdk_keycode = key->hardware_keycode;
  802. int qemu_keycode;
  803. int i;
  804. if (key->keyval == GDK_KEY_Pause) {
  805. qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE,
  806. key->type == GDK_KEY_PRESS);
  807. return TRUE;
  808. }
  809. qemu_keycode = gd_map_keycode(s, gtk_widget_get_display(widget),
  810. gdk_keycode);
  811. trace_gd_key_event(vc->label, gdk_keycode, qemu_keycode,
  812. (key->type == GDK_KEY_PRESS) ? "down" : "up");
  813. for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
  814. if (qemu_keycode == modifier_keycode[i]) {
  815. s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS);
  816. }
  817. }
  818. qemu_input_event_send_key_number(vc->gfx.dcl.con, qemu_keycode,
  819. key->type == GDK_KEY_PRESS);
  820. return TRUE;
  821. }
  822. static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque)
  823. {
  824. if (event->type == GDK_MOTION_NOTIFY) {
  825. return gd_motion_event(widget, &event->motion, opaque);
  826. }
  827. return FALSE;
  828. }
  829. /** Window Menu Actions **/
  830. static void gd_menu_pause(GtkMenuItem *item, void *opaque)
  831. {
  832. GtkDisplayState *s = opaque;
  833. if (s->external_pause_update) {
  834. return;
  835. }
  836. if (runstate_is_running()) {
  837. qmp_stop(NULL);
  838. } else {
  839. qmp_cont(NULL);
  840. }
  841. }
  842. static void gd_menu_reset(GtkMenuItem *item, void *opaque)
  843. {
  844. qmp_system_reset(NULL);
  845. }
  846. static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
  847. {
  848. qmp_system_powerdown(NULL);
  849. }
  850. static void gd_menu_quit(GtkMenuItem *item, void *opaque)
  851. {
  852. qmp_quit(NULL);
  853. }
  854. static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
  855. {
  856. GtkDisplayState *s = opaque;
  857. VirtualConsole *vc = gd_vc_find_by_menu(s);
  858. GtkNotebook *nb = GTK_NOTEBOOK(s->notebook);
  859. GtkWidget *child;
  860. gint page;
  861. gtk_release_modifiers(s);
  862. if (vc) {
  863. page = gtk_notebook_page_num(nb, vc->tab_item);
  864. gtk_notebook_set_current_page(nb, page);
  865. child = gtk_notebook_get_nth_page(nb, page);
  866. gtk_widget_grab_focus(child);
  867. }
  868. }
  869. static void gd_accel_switch_vc(void *opaque)
  870. {
  871. VirtualConsole *vc = opaque;
  872. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
  873. }
  874. static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
  875. {
  876. GtkDisplayState *s = opaque;
  877. VirtualConsole *vc = gd_vc_find_current(s);
  878. if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
  879. gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
  880. } else {
  881. gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
  882. }
  883. gd_update_windowsize(vc);
  884. }
  885. static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
  886. void *opaque)
  887. {
  888. VirtualConsole *vc = opaque;
  889. GtkDisplayState *s = vc->s;
  890. gtk_widget_set_sensitive(vc->menu_item, true);
  891. gd_widget_reparent(vc->window, s->notebook, vc->tab_item);
  892. gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
  893. vc->tab_item, vc->label);
  894. gtk_widget_destroy(vc->window);
  895. vc->window = NULL;
  896. return TRUE;
  897. }
  898. static gboolean gd_win_grab(void *opaque)
  899. {
  900. VirtualConsole *vc = opaque;
  901. fprintf(stderr, "%s: %s\n", __func__, vc->label);
  902. if (vc->s->ptr_owner) {
  903. gd_ungrab_pointer(vc->s);
  904. } else {
  905. gd_grab_pointer(vc);
  906. }
  907. gd_update_caption(vc->s);
  908. return TRUE;
  909. }
  910. static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
  911. {
  912. GtkDisplayState *s = opaque;
  913. VirtualConsole *vc = gd_vc_find_current(s);
  914. if (vc->type == GD_VC_GFX) {
  915. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
  916. FALSE);
  917. }
  918. if (!vc->window) {
  919. gtk_widget_set_sensitive(vc->menu_item, false);
  920. vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  921. gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
  922. g_signal_connect(vc->window, "delete-event",
  923. G_CALLBACK(gd_tab_window_close), vc);
  924. gtk_widget_show_all(vc->window);
  925. GtkAccelGroup *ag = gtk_accel_group_new();
  926. gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
  927. GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab), vc, NULL);
  928. gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
  929. gd_update_geometry_hints(vc);
  930. gd_update_caption(s);
  931. }
  932. }
  933. static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
  934. {
  935. GtkDisplayState *s = opaque;
  936. VirtualConsole *vc = gd_vc_find_current(s);
  937. if (!s->full_screen) {
  938. gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
  939. gtk_widget_hide(s->menu_bar);
  940. if (vc->type == GD_VC_GFX) {
  941. gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
  942. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
  943. TRUE);
  944. }
  945. gtk_window_fullscreen(GTK_WINDOW(s->window));
  946. s->full_screen = TRUE;
  947. } else {
  948. gtk_window_unfullscreen(GTK_WINDOW(s->window));
  949. gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
  950. gtk_widget_show(s->menu_bar);
  951. s->full_screen = FALSE;
  952. if (vc->type == GD_VC_GFX) {
  953. vc->gfx.scale_x = 1.0;
  954. vc->gfx.scale_y = 1.0;
  955. gd_update_windowsize(vc);
  956. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
  957. FALSE);
  958. }
  959. }
  960. gd_update_cursor(vc);
  961. }
  962. static void gd_accel_full_screen(void *opaque)
  963. {
  964. GtkDisplayState *s = opaque;
  965. gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
  966. }
  967. static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
  968. {
  969. GtkDisplayState *s = opaque;
  970. VirtualConsole *vc = gd_vc_find_current(s);
  971. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
  972. FALSE);
  973. vc->gfx.scale_x += VC_SCALE_STEP;
  974. vc->gfx.scale_y += VC_SCALE_STEP;
  975. gd_update_windowsize(vc);
  976. }
  977. static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
  978. {
  979. GtkDisplayState *s = opaque;
  980. VirtualConsole *vc = gd_vc_find_current(s);
  981. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
  982. FALSE);
  983. vc->gfx.scale_x -= VC_SCALE_STEP;
  984. vc->gfx.scale_y -= VC_SCALE_STEP;
  985. vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN);
  986. vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN);
  987. gd_update_windowsize(vc);
  988. }
  989. static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
  990. {
  991. GtkDisplayState *s = opaque;
  992. VirtualConsole *vc = gd_vc_find_current(s);
  993. vc->gfx.scale_x = 1.0;
  994. vc->gfx.scale_y = 1.0;
  995. gd_update_windowsize(vc);
  996. }
  997. static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
  998. {
  999. GtkDisplayState *s = opaque;
  1000. VirtualConsole *vc = gd_vc_find_current(s);
  1001. if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
  1002. s->free_scale = TRUE;
  1003. } else {
  1004. s->free_scale = FALSE;
  1005. vc->gfx.scale_x = 1.0;
  1006. vc->gfx.scale_y = 1.0;
  1007. }
  1008. gd_update_windowsize(vc);
  1009. gd_update_full_redraw(vc);
  1010. }
  1011. #if GTK_CHECK_VERSION(3, 0, 0)
  1012. static void gd_grab_devices(VirtualConsole *vc, bool grab,
  1013. GdkInputSource source, GdkEventMask mask,
  1014. GdkCursor *cursor)
  1015. {
  1016. GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
  1017. GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
  1018. GList *devs = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER);
  1019. GList *tmp = devs;
  1020. for (tmp = devs; tmp; tmp = tmp->next) {
  1021. GdkDevice *dev = tmp->data;
  1022. if (gdk_device_get_source(dev) != source) {
  1023. continue;
  1024. }
  1025. if (grab) {
  1026. GdkWindow *win = gtk_widget_get_window(vc->gfx.drawing_area);
  1027. gdk_device_grab(dev, win, GDK_OWNERSHIP_NONE, FALSE,
  1028. mask, cursor, GDK_CURRENT_TIME);
  1029. } else {
  1030. gdk_device_ungrab(dev, GDK_CURRENT_TIME);
  1031. }
  1032. }
  1033. g_list_free(devs);
  1034. }
  1035. #endif
  1036. static void gd_grab_keyboard(VirtualConsole *vc)
  1037. {
  1038. #if GTK_CHECK_VERSION(3, 0, 0)
  1039. gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD,
  1040. GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
  1041. NULL);
  1042. #else
  1043. gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area),
  1044. FALSE,
  1045. GDK_CURRENT_TIME);
  1046. #endif
  1047. vc->s->kbd_owner = vc;
  1048. trace_gd_grab(vc->label, "kbd", true);
  1049. }
  1050. static void gd_ungrab_keyboard(GtkDisplayState *s)
  1051. {
  1052. VirtualConsole *vc = s->kbd_owner;
  1053. if (vc == NULL) {
  1054. return;
  1055. }
  1056. s->kbd_owner = NULL;
  1057. #if GTK_CHECK_VERSION(3, 0, 0)
  1058. gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL);
  1059. #else
  1060. gdk_keyboard_ungrab(GDK_CURRENT_TIME);
  1061. #endif
  1062. trace_gd_grab(vc->label, "kbd", false);
  1063. }
  1064. static void gd_grab_pointer(VirtualConsole *vc)
  1065. {
  1066. GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
  1067. #if GTK_CHECK_VERSION(3, 0, 0)
  1068. GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
  1069. gd_grab_devices(vc, true, GDK_SOURCE_MOUSE,
  1070. GDK_POINTER_MOTION_MASK |
  1071. GDK_BUTTON_PRESS_MASK |
  1072. GDK_BUTTON_RELEASE_MASK |
  1073. GDK_BUTTON_MOTION_MASK |
  1074. GDK_SCROLL_MASK,
  1075. vc->s->null_cursor);
  1076. gdk_device_get_position(gdk_device_manager_get_client_pointer(mgr),
  1077. NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
  1078. #else
  1079. gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area),
  1080. FALSE, /* All events to come to our window directly */
  1081. GDK_POINTER_MOTION_MASK |
  1082. GDK_BUTTON_PRESS_MASK |
  1083. GDK_BUTTON_RELEASE_MASK |
  1084. GDK_BUTTON_MOTION_MASK |
  1085. GDK_SCROLL_MASK,
  1086. NULL, /* Allow cursor to move over entire desktop */
  1087. vc->s->null_cursor,
  1088. GDK_CURRENT_TIME);
  1089. gdk_display_get_pointer(display, NULL,
  1090. &vc->s->grab_x_root, &vc->s->grab_y_root, NULL);
  1091. #endif
  1092. vc->s->ptr_owner = vc;
  1093. trace_gd_grab(vc->label, "ptr", true);
  1094. }
  1095. static void gd_ungrab_pointer(GtkDisplayState *s)
  1096. {
  1097. VirtualConsole *vc = s->ptr_owner;
  1098. if (vc == NULL) {
  1099. return;
  1100. }
  1101. s->ptr_owner = NULL;
  1102. GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
  1103. #if GTK_CHECK_VERSION(3, 0, 0)
  1104. GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
  1105. gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL);
  1106. gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
  1107. gtk_widget_get_screen(vc->gfx.drawing_area),
  1108. vc->s->grab_x_root, vc->s->grab_y_root);
  1109. #else
  1110. gdk_pointer_ungrab(GDK_CURRENT_TIME);
  1111. gdk_display_warp_pointer(display,
  1112. gtk_widget_get_screen(vc->gfx.drawing_area),
  1113. vc->s->grab_x_root, vc->s->grab_y_root);
  1114. #endif
  1115. trace_gd_grab(vc->label, "ptr", false);
  1116. }
  1117. static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
  1118. {
  1119. GtkDisplayState *s = opaque;
  1120. VirtualConsole *vc = gd_vc_find_current(s);
  1121. if (gd_is_grab_active(s)) {
  1122. if (!gd_grab_on_hover(s)) {
  1123. gd_grab_keyboard(vc);
  1124. }
  1125. gd_grab_pointer(vc);
  1126. } else {
  1127. gd_ungrab_keyboard(s);
  1128. gd_ungrab_pointer(s);
  1129. }
  1130. gd_update_caption(s);
  1131. gd_update_cursor(vc);
  1132. }
  1133. static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
  1134. gpointer data)
  1135. {
  1136. GtkDisplayState *s = data;
  1137. VirtualConsole *vc;
  1138. gboolean on_vga;
  1139. if (!gtk_widget_get_realized(s->notebook)) {
  1140. return;
  1141. }
  1142. #ifdef VTE_RESIZE_HACK
  1143. vc = gd_vc_find_current(s);
  1144. if (vc && vc->type == GD_VC_VTE) {
  1145. gtk_widget_hide(vc->vte.terminal);
  1146. }
  1147. #endif
  1148. vc = gd_vc_find_by_page(s, arg2);
  1149. if (!vc) {
  1150. return;
  1151. }
  1152. #ifdef VTE_RESIZE_HACK
  1153. if (vc->type == GD_VC_VTE) {
  1154. gtk_widget_show(vc->vte.terminal);
  1155. }
  1156. #endif
  1157. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item),
  1158. TRUE);
  1159. on_vga = (vc->type == GD_VC_GFX);
  1160. if (!on_vga) {
  1161. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
  1162. FALSE);
  1163. } else if (s->full_screen) {
  1164. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
  1165. TRUE);
  1166. }
  1167. gtk_widget_set_sensitive(s->grab_item, on_vga);
  1168. gd_update_windowsize(vc);
  1169. gd_update_cursor(vc);
  1170. }
  1171. static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
  1172. gpointer opaque)
  1173. {
  1174. VirtualConsole *vc = opaque;
  1175. GtkDisplayState *s = vc->s;
  1176. if (gd_grab_on_hover(s)) {
  1177. gd_ungrab_keyboard(s);
  1178. gd_grab_keyboard(vc);
  1179. gd_update_caption(s);
  1180. }
  1181. return TRUE;
  1182. }
  1183. static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
  1184. gpointer opaque)
  1185. {
  1186. VirtualConsole *vc = opaque;
  1187. GtkDisplayState *s = vc->s;
  1188. if (gd_grab_on_hover(s)) {
  1189. gd_ungrab_keyboard(s);
  1190. gd_update_caption(s);
  1191. }
  1192. return TRUE;
  1193. }
  1194. static gboolean gd_focus_out_event(GtkWidget *widget,
  1195. GdkEventCrossing *crossing, gpointer opaque)
  1196. {
  1197. VirtualConsole *vc = opaque;
  1198. GtkDisplayState *s = vc->s;
  1199. gtk_release_modifiers(s);
  1200. return TRUE;
  1201. }
  1202. /** Virtual Console Callbacks **/
  1203. static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
  1204. int idx, GSList *group, GtkWidget *view_menu)
  1205. {
  1206. vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label);
  1207. gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx,
  1208. HOTKEY_MODIFIERS, 0,
  1209. g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL));
  1210. #if GTK_CHECK_VERSION(3, 8, 0)
  1211. gtk_accel_label_set_accel(
  1212. GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))),
  1213. GDK_KEY_1 + idx, HOTKEY_MODIFIERS);
  1214. #endif
  1215. g_signal_connect(vc->menu_item, "activate",
  1216. G_CALLBACK(gd_menu_switch_vc), s);
  1217. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
  1218. group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
  1219. return group;
  1220. }
  1221. #if defined(CONFIG_VTE)
  1222. static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
  1223. {
  1224. VirtualConsole *vc = opaque;
  1225. if (gtk_adjustment_get_upper(adjustment) >
  1226. gtk_adjustment_get_page_size(adjustment)) {
  1227. gtk_widget_show(vc->vte.scrollbar);
  1228. } else {
  1229. gtk_widget_hide(vc->vte.scrollbar);
  1230. }
  1231. }
  1232. static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
  1233. {
  1234. VirtualConsole *vc = chr->opaque;
  1235. vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
  1236. return len;
  1237. }
  1238. static int nb_vcs;
  1239. static CharDriverState *vcs[MAX_VCS];
  1240. static CharDriverState *gd_vc_handler(ChardevVC *unused)
  1241. {
  1242. CharDriverState *chr;
  1243. chr = g_malloc0(sizeof(*chr));
  1244. chr->chr_write = gd_vc_chr_write;
  1245. /* defer OPENED events until our vc is fully initialized */
  1246. chr->explicit_be_open = true;
  1247. vcs[nb_vcs++] = chr;
  1248. return chr;
  1249. }
  1250. static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
  1251. gpointer user_data)
  1252. {
  1253. VirtualConsole *vc = user_data;
  1254. qemu_chr_be_write(vc->vte.chr, (uint8_t *)text, (unsigned int)size);
  1255. return TRUE;
  1256. }
  1257. static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
  1258. CharDriverState *chr, int idx,
  1259. GSList *group, GtkWidget *view_menu)
  1260. {
  1261. char buffer[32];
  1262. GtkWidget *box;
  1263. GtkWidget *scrollbar;
  1264. GtkAdjustment *vadjustment;
  1265. vc->s = s;
  1266. vc->vte.chr = chr;
  1267. snprintf(buffer, sizeof(buffer), "vc%d", idx);
  1268. vc->label = g_strdup_printf("%s", vc->vte.chr->label
  1269. ? vc->vte.chr->label : buffer);
  1270. group = gd_vc_menu_init(s, vc, idx, group, view_menu);
  1271. vc->vte.terminal = vte_terminal_new();
  1272. g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc);
  1273. vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1);
  1274. vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal),
  1275. VC_TERM_X_MIN, VC_TERM_Y_MIN);
  1276. #if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0)
  1277. vadjustment = gtk_scrollable_get_vadjustment
  1278. (GTK_SCROLLABLE(vc->vte.terminal));
  1279. #else
  1280. vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
  1281. #endif
  1282. #if GTK_CHECK_VERSION(3, 0, 0)
  1283. box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
  1284. scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment);
  1285. #else
  1286. box = gtk_hbox_new(false, 2);
  1287. scrollbar = gtk_vscrollbar_new(vadjustment);
  1288. #endif
  1289. gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0);
  1290. gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0);
  1291. vc->vte.chr->opaque = vc;
  1292. vc->vte.box = box;
  1293. vc->vte.scrollbar = scrollbar;
  1294. g_signal_connect(vadjustment, "changed",
  1295. G_CALLBACK(gd_vc_adjustment_changed), vc);
  1296. vc->type = GD_VC_VTE;
  1297. vc->tab_item = box;
  1298. gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
  1299. gtk_label_new(vc->label));
  1300. qemu_chr_be_generic_open(vc->vte.chr);
  1301. if (vc->vte.chr->init) {
  1302. vc->vte.chr->init(vc->vte.chr);
  1303. }
  1304. return group;
  1305. }
  1306. static void gd_vcs_init(GtkDisplayState *s, GSList *group,
  1307. GtkWidget *view_menu)
  1308. {
  1309. int i;
  1310. for (i = 0; i < nb_vcs; i++) {
  1311. VirtualConsole *vc = &s->vc[s->nb_vcs];
  1312. group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu);
  1313. s->nb_vcs++;
  1314. }
  1315. }
  1316. #endif /* CONFIG_VTE */
  1317. /** Window Creation **/
  1318. static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
  1319. {
  1320. #if GTK_CHECK_VERSION(3, 0, 0)
  1321. g_signal_connect(vc->gfx.drawing_area, "draw",
  1322. G_CALLBACK(gd_draw_event), vc);
  1323. #else
  1324. g_signal_connect(vc->gfx.drawing_area, "expose-event",
  1325. G_CALLBACK(gd_expose_event), vc);
  1326. #endif
  1327. g_signal_connect(vc->gfx.drawing_area, "event",
  1328. G_CALLBACK(gd_event), vc);
  1329. g_signal_connect(vc->gfx.drawing_area, "button-press-event",
  1330. G_CALLBACK(gd_button_event), vc);
  1331. g_signal_connect(vc->gfx.drawing_area, "button-release-event",
  1332. G_CALLBACK(gd_button_event), vc);
  1333. g_signal_connect(vc->gfx.drawing_area, "scroll-event",
  1334. G_CALLBACK(gd_scroll_event), vc);
  1335. g_signal_connect(vc->gfx.drawing_area, "key-press-event",
  1336. G_CALLBACK(gd_key_event), vc);
  1337. g_signal_connect(vc->gfx.drawing_area, "key-release-event",
  1338. G_CALLBACK(gd_key_event), vc);
  1339. g_signal_connect(vc->gfx.drawing_area, "enter-notify-event",
  1340. G_CALLBACK(gd_enter_event), vc);
  1341. g_signal_connect(vc->gfx.drawing_area, "leave-notify-event",
  1342. G_CALLBACK(gd_leave_event), vc);
  1343. g_signal_connect(vc->gfx.drawing_area, "focus-out-event",
  1344. G_CALLBACK(gd_focus_out_event), vc);
  1345. }
  1346. static void gd_connect_signals(GtkDisplayState *s)
  1347. {
  1348. g_signal_connect(s->show_tabs_item, "activate",
  1349. G_CALLBACK(gd_menu_show_tabs), s);
  1350. g_signal_connect(s->untabify_item, "activate",
  1351. G_CALLBACK(gd_menu_untabify), s);
  1352. g_signal_connect(s->window, "delete-event",
  1353. G_CALLBACK(gd_window_close), s);
  1354. g_signal_connect(s->pause_item, "activate",
  1355. G_CALLBACK(gd_menu_pause), s);
  1356. g_signal_connect(s->reset_item, "activate",
  1357. G_CALLBACK(gd_menu_reset), s);
  1358. g_signal_connect(s->powerdown_item, "activate",
  1359. G_CALLBACK(gd_menu_powerdown), s);
  1360. g_signal_connect(s->quit_item, "activate",
  1361. G_CALLBACK(gd_menu_quit), s);
  1362. g_signal_connect(s->full_screen_item, "activate",
  1363. G_CALLBACK(gd_menu_full_screen), s);
  1364. g_signal_connect(s->zoom_in_item, "activate",
  1365. G_CALLBACK(gd_menu_zoom_in), s);
  1366. g_signal_connect(s->zoom_out_item, "activate",
  1367. G_CALLBACK(gd_menu_zoom_out), s);
  1368. g_signal_connect(s->zoom_fixed_item, "activate",
  1369. G_CALLBACK(gd_menu_zoom_fixed), s);
  1370. g_signal_connect(s->zoom_fit_item, "activate",
  1371. G_CALLBACK(gd_menu_zoom_fit), s);
  1372. g_signal_connect(s->grab_item, "activate",
  1373. G_CALLBACK(gd_menu_grab_input), s);
  1374. g_signal_connect(s->notebook, "switch-page",
  1375. G_CALLBACK(gd_change_page), s);
  1376. }
  1377. static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
  1378. {
  1379. GtkWidget *machine_menu;
  1380. GtkWidget *separator;
  1381. machine_menu = gtk_menu_new();
  1382. gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group);
  1383. s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
  1384. gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item);
  1385. separator = gtk_separator_menu_item_new();
  1386. gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
  1387. s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset"));
  1388. gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item);
  1389. s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down"));
  1390. gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item);
  1391. separator = gtk_separator_menu_item_new();
  1392. gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
  1393. s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
  1394. gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
  1395. "<QEMU>/Machine/Quit");
  1396. gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
  1397. GDK_KEY_q, HOTKEY_MODIFIERS);
  1398. gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
  1399. return machine_menu;
  1400. }
  1401. static const DisplayChangeListenerOps dcl_ops = {
  1402. .dpy_name = "gtk",
  1403. .dpy_gfx_update = gd_update,
  1404. .dpy_gfx_switch = gd_switch,
  1405. .dpy_refresh = gd_refresh,
  1406. .dpy_mouse_set = gd_mouse_set,
  1407. .dpy_cursor_define = gd_cursor_define,
  1408. };
  1409. static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
  1410. QemuConsole *con, int idx,
  1411. GSList *group, GtkWidget *view_menu)
  1412. {
  1413. Object *obj;
  1414. obj = object_property_get_link(OBJECT(con), "device", NULL);
  1415. if (obj) {
  1416. vc->label = g_strdup_printf("%s", object_get_typename(obj));
  1417. } else {
  1418. vc->label = g_strdup_printf("VGA");
  1419. }
  1420. vc->s = s;
  1421. vc->gfx.scale_x = 1.0;
  1422. vc->gfx.scale_y = 1.0;
  1423. vc->gfx.drawing_area = gtk_drawing_area_new();
  1424. gtk_widget_add_events(vc->gfx.drawing_area,
  1425. GDK_POINTER_MOTION_MASK |
  1426. GDK_BUTTON_PRESS_MASK |
  1427. GDK_BUTTON_RELEASE_MASK |
  1428. GDK_BUTTON_MOTION_MASK |
  1429. GDK_ENTER_NOTIFY_MASK |
  1430. GDK_LEAVE_NOTIFY_MASK |
  1431. GDK_SCROLL_MASK |
  1432. GDK_KEY_PRESS_MASK);
  1433. gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
  1434. vc->type = GD_VC_GFX;
  1435. vc->tab_item = vc->gfx.drawing_area;
  1436. gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
  1437. vc->tab_item, gtk_label_new(vc->label));
  1438. gd_connect_vc_gfx_signals(vc);
  1439. group = gd_vc_menu_init(s, vc, idx, group, view_menu);
  1440. vc->gfx.dcl.ops = &dcl_ops;
  1441. vc->gfx.dcl.con = con;
  1442. register_displaychangelistener(&vc->gfx.dcl);
  1443. return group;
  1444. }
  1445. static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
  1446. {
  1447. GSList *group = NULL;
  1448. GtkWidget *view_menu;
  1449. GtkWidget *separator;
  1450. QemuConsole *con;
  1451. int vc;
  1452. view_menu = gtk_menu_new();
  1453. gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group);
  1454. s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen"));
  1455. gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0,
  1456. g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL));
  1457. #if GTK_CHECK_VERSION(3, 8, 0)
  1458. gtk_accel_label_set_accel(
  1459. GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))),
  1460. GDK_KEY_f, HOTKEY_MODIFIERS);
  1461. #endif
  1462. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item);
  1463. separator = gtk_separator_menu_item_new();
  1464. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
  1465. s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In"));
  1466. gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
  1467. "<QEMU>/View/Zoom In");
  1468. gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus,
  1469. HOTKEY_MODIFIERS);
  1470. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
  1471. s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out"));
  1472. gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
  1473. "<QEMU>/View/Zoom Out");
  1474. gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus,
  1475. HOTKEY_MODIFIERS);
  1476. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item);
  1477. s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit"));
  1478. gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
  1479. "<QEMU>/View/Zoom Fixed");
  1480. gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0,
  1481. HOTKEY_MODIFIERS);
  1482. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item);
  1483. s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
  1484. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item);
  1485. separator = gtk_separator_menu_item_new();
  1486. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
  1487. s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
  1488. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item);
  1489. s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
  1490. gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
  1491. "<QEMU>/View/Grab Input");
  1492. gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g,
  1493. HOTKEY_MODIFIERS);
  1494. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item);
  1495. separator = gtk_separator_menu_item_new();
  1496. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
  1497. /* gfx */
  1498. for (vc = 0;; vc++) {
  1499. con = qemu_console_lookup_by_index(vc);
  1500. if (!con || !qemu_console_is_graphic(con)) {
  1501. break;
  1502. }
  1503. group = gd_vc_gfx_init(s, &s->vc[vc], con,
  1504. vc, group, view_menu);
  1505. s->nb_vcs++;
  1506. }
  1507. #if defined(CONFIG_VTE)
  1508. /* vte */
  1509. gd_vcs_init(s, group, view_menu);
  1510. #endif
  1511. separator = gtk_separator_menu_item_new();
  1512. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
  1513. s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
  1514. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
  1515. s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
  1516. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);
  1517. return view_menu;
  1518. }
  1519. static void gd_create_menus(GtkDisplayState *s)
  1520. {
  1521. s->accel_group = gtk_accel_group_new();
  1522. s->machine_menu = gd_create_menu_machine(s);
  1523. s->view_menu = gd_create_menu_view(s);
  1524. s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
  1525. gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
  1526. s->machine_menu);
  1527. gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
  1528. s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
  1529. gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
  1530. gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
  1531. g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group);
  1532. gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group);
  1533. }
  1534. static void gd_set_keycode_type(GtkDisplayState *s)
  1535. {
  1536. #ifdef GDK_WINDOWING_X11
  1537. GdkDisplay *display = gtk_widget_get_display(s->window);
  1538. if (GDK_IS_X11_DISPLAY(display)) {
  1539. Display *x11_display = gdk_x11_display_get_xdisplay(display);
  1540. XkbDescPtr desc = XkbGetKeyboard(x11_display, XkbGBN_AllComponentsMask,
  1541. XkbUseCoreKbd);
  1542. char *keycodes = NULL;
  1543. if (desc && desc->names) {
  1544. keycodes = XGetAtomName(x11_display, desc->names->keycodes);
  1545. }
  1546. if (keycodes == NULL) {
  1547. fprintf(stderr, "could not lookup keycode name\n");
  1548. } else if (strstart(keycodes, "evdev", NULL)) {
  1549. s->has_evdev = true;
  1550. } else if (!strstart(keycodes, "xfree86", NULL)) {
  1551. fprintf(stderr, "unknown keycodes `%s', please report to "
  1552. "qemu-devel@nongnu.org\n", keycodes);
  1553. }
  1554. if (desc) {
  1555. XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
  1556. }
  1557. if (keycodes) {
  1558. XFree(keycodes);
  1559. }
  1560. }
  1561. #endif
  1562. }
  1563. void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
  1564. {
  1565. GtkDisplayState *s = g_malloc0(sizeof(*s));
  1566. char *filename;
  1567. gtk_init(NULL, NULL);
  1568. s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  1569. #if GTK_CHECK_VERSION(3, 2, 0)
  1570. s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
  1571. #else
  1572. s->vbox = gtk_vbox_new(FALSE, 0);
  1573. #endif
  1574. s->notebook = gtk_notebook_new();
  1575. s->menu_bar = gtk_menu_bar_new();
  1576. s->free_scale = FALSE;
  1577. setlocale(LC_ALL, "");
  1578. bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
  1579. textdomain("qemu");
  1580. s->null_cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
  1581. s->mouse_mode_notifier.notify = gd_mouse_mode_change;
  1582. qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
  1583. qemu_add_vm_change_state_handler(gd_change_runstate, s);
  1584. filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg");
  1585. if (filename) {
  1586. GError *error = NULL;
  1587. GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error);
  1588. if (pixbuf) {
  1589. gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf);
  1590. } else {
  1591. g_error_free(error);
  1592. }
  1593. g_free(filename);
  1594. }
  1595. gd_create_menus(s);
  1596. gd_connect_signals(s);
  1597. gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
  1598. gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
  1599. gd_update_caption(s);
  1600. gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
  1601. gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
  1602. gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
  1603. gtk_widget_show_all(s->window);
  1604. #ifdef VTE_RESIZE_HACK
  1605. {
  1606. VirtualConsole *cur = gd_vc_find_current(s);
  1607. if (cur) {
  1608. int i;
  1609. for (i = 0; i < s->nb_vcs; i++) {
  1610. VirtualConsole *vc = &s->vc[i];
  1611. if (vc && vc->type == GD_VC_VTE && vc != cur) {
  1612. gtk_widget_hide(vc->vte.terminal);
  1613. }
  1614. }
  1615. gd_update_windowsize(cur);
  1616. }
  1617. }
  1618. #endif
  1619. if (full_screen) {
  1620. gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
  1621. }
  1622. if (grab_on_hover) {
  1623. gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
  1624. }
  1625. gd_set_keycode_type(s);
  1626. }
  1627. void early_gtk_display_init(void)
  1628. {
  1629. #if defined(CONFIG_VTE)
  1630. register_vc_handler(gd_vc_handler);
  1631. #endif
  1632. }