* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "qemu-common.h"
-#include "console.h"
-#include "sysemu.h"
-
#include <SDL.h>
+#include <SDL_syswm.h>
#ifndef _WIN32
#include <signal.h>
#endif
-static SDL_Surface *screen;
+#include "qemu-common.h"
+#include "console.h"
+#include "sysemu.h"
+#include "x_keymap.h"
+#include "sdl_zoom.h"
+
+static DisplayChangeListener *dcl;
+static SDL_Surface *real_screen;
+static SDL_Surface *guest_screen = NULL;
static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
static int last_vm_running;
static int gui_saved_grab;
static int absolute_enabled = 0;
static int guest_cursor = 0;
static int guest_x, guest_y;
-static SDL_Cursor *guest_sprite = 0;
+static SDL_Cursor *guest_sprite = NULL;
+static uint8_t allocator;
+static SDL_PixelFormat host_format;
+static int scaling_active = 0;
+static Notifier mouse_mode_notifier;
static void sdl_update(DisplayState *ds, int x, int y, int w, int h)
{
// printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
- SDL_UpdateRect(screen, x, y, w, h);
+ SDL_Rect rec;
+ rec.x = x;
+ rec.y = y;
+ rec.w = w;
+ rec.h = h;
+
+ if (guest_screen) {
+ if (!scaling_active) {
+ SDL_BlitSurface(guest_screen, &rec, real_screen, &rec);
+ } else {
+ if (sdl_zoom_blit(guest_screen, real_screen, SMOOTHING_ON, &rec) < 0) {
+ fprintf(stderr, "Zoom blit failed\n");
+ exit(1);
+ }
+ }
+ }
+ SDL_UpdateRect(real_screen, rec.x, rec.y, rec.w, rec.h);
}
-static void sdl_resize(DisplayState *ds, int w, int h)
+static void sdl_setdata(DisplayState *ds)
+{
+ SDL_Rect rec;
+ rec.x = 0;
+ rec.y = 0;
+ rec.w = real_screen->w;
+ rec.h = real_screen->h;
+
+ if (guest_screen != NULL) SDL_FreeSurface(guest_screen);
+
+ guest_screen = SDL_CreateRGBSurfaceFrom(ds_get_data(ds), ds_get_width(ds), ds_get_height(ds),
+ ds_get_bits_per_pixel(ds), ds_get_linesize(ds),
+ ds->surface->pf.rmask, ds->surface->pf.gmask,
+ ds->surface->pf.bmask, ds->surface->pf.amask);
+}
+
+static void do_sdl_resize(int new_width, int new_height, int bpp)
{
int flags;
// printf("resizing to %d %d\n", w, h);
- flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL;
+ flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_RESIZABLE;
if (gui_fullscreen)
flags |= SDL_FULLSCREEN;
if (gui_noframe)
flags |= SDL_NOFRAME;
- width = w;
- height = h;
-
- again:
- screen = SDL_SetVideoMode(w, h, 0, flags);
- if (!screen) {
- fprintf(stderr, "Could not open SDL display\n");
+ width = new_width;
+ height = new_height;
+ real_screen = SDL_SetVideoMode(width, height, bpp, flags);
+ if (!real_screen) {
+ fprintf(stderr, "Could not open SDL display (%dx%dx%d): %s\n", width,
+ height, bpp, SDL_GetError());
exit(1);
}
- if (!screen->pixels && (flags & SDL_HWSURFACE) && (flags & SDL_FULLSCREEN)) {
- flags &= ~SDL_HWSURFACE;
- goto again;
+}
+
+static void sdl_resize(DisplayState *ds)
+{
+ if (!allocator) {
+ if (!scaling_active)
+ do_sdl_resize(ds_get_width(ds), ds_get_height(ds), 0);
+ else if (real_screen->format->BitsPerPixel != ds_get_bits_per_pixel(ds))
+ do_sdl_resize(real_screen->w, real_screen->h, ds_get_bits_per_pixel(ds));
+ sdl_setdata(ds);
+ } else {
+ if (guest_screen != NULL) {
+ SDL_FreeSurface(guest_screen);
+ guest_screen = NULL;
+ }
}
+}
+
+static PixelFormat sdl_to_qemu_pixelformat(SDL_PixelFormat *sdl_pf)
+{
+ PixelFormat qemu_pf;
- if (!screen->pixels) {
- fprintf(stderr, "Could not open SDL display\n");
+ memset(&qemu_pf, 0x00, sizeof(PixelFormat));
+
+ qemu_pf.bits_per_pixel = sdl_pf->BitsPerPixel;
+ qemu_pf.bytes_per_pixel = sdl_pf->BytesPerPixel;
+ qemu_pf.depth = (qemu_pf.bits_per_pixel) == 32 ? 24 : (qemu_pf.bits_per_pixel);
+
+ qemu_pf.rmask = sdl_pf->Rmask;
+ qemu_pf.gmask = sdl_pf->Gmask;
+ qemu_pf.bmask = sdl_pf->Bmask;
+ qemu_pf.amask = sdl_pf->Amask;
+
+ qemu_pf.rshift = sdl_pf->Rshift;
+ qemu_pf.gshift = sdl_pf->Gshift;
+ qemu_pf.bshift = sdl_pf->Bshift;
+ qemu_pf.ashift = sdl_pf->Ashift;
+
+ qemu_pf.rbits = 8 - sdl_pf->Rloss;
+ qemu_pf.gbits = 8 - sdl_pf->Gloss;
+ qemu_pf.bbits = 8 - sdl_pf->Bloss;
+ qemu_pf.abits = 8 - sdl_pf->Aloss;
+
+ qemu_pf.rmax = ((1 << qemu_pf.rbits) - 1);
+ qemu_pf.gmax = ((1 << qemu_pf.gbits) - 1);
+ qemu_pf.bmax = ((1 << qemu_pf.bbits) - 1);
+ qemu_pf.amax = ((1 << qemu_pf.abits) - 1);
+
+ return qemu_pf;
+}
+
+static DisplaySurface* sdl_create_displaysurface(int width, int height)
+{
+ DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
+ if (surface == NULL) {
+ fprintf(stderr, "sdl_create_displaysurface: malloc failed\n");
exit(1);
}
- ds->data = screen->pixels;
- ds->linesize = screen->pitch;
- ds->depth = screen->format->BitsPerPixel;
- if (ds->depth == 32 && screen->format->Rshift == 0) {
- ds->bgr = 1;
- } else {
- ds->bgr = 0;
+
+ surface->width = width;
+ surface->height = height;
+
+ if (scaling_active) {
+ if (host_format.BytesPerPixel != 2 && host_format.BytesPerPixel != 4) {
+ surface->linesize = width * 4;
+ surface->pf = qemu_default_pixelformat(32);
+ } else {
+ surface->linesize = width * host_format.BytesPerPixel;
+ surface->pf = sdl_to_qemu_pixelformat(&host_format);
+ }
+#ifdef HOST_WORDS_BIGENDIAN
+ surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
+#else
+ surface->flags = QEMU_ALLOCATED_FLAG;
+#endif
+ surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height);
+
+ return surface;
}
- ds->width = w;
- ds->height = h;
+
+ if (host_format.BitsPerPixel == 16)
+ do_sdl_resize(width, height, 16);
+ else
+ do_sdl_resize(width, height, 32);
+
+ surface->pf = sdl_to_qemu_pixelformat(real_screen->format);
+ surface->linesize = real_screen->pitch;
+ surface->data = real_screen->pixels;
+
+#ifdef HOST_WORDS_BIGENDIAN
+ surface->flags = QEMU_REALPIXELS_FLAG | QEMU_BIG_ENDIAN_FLAG;
+#else
+ surface->flags = QEMU_REALPIXELS_FLAG;
+#endif
+ allocator = 1;
+
+ return surface;
+}
+
+static void sdl_free_displaysurface(DisplaySurface *surface)
+{
+ allocator = 0;
+ if (surface == NULL)
+ return;
+
+ if (surface->flags & QEMU_ALLOCATED_FLAG)
+ qemu_free(surface->data);
+ qemu_free(surface);
+}
+
+static DisplaySurface* sdl_resize_displaysurface(DisplaySurface *surface, int width, int height)
+{
+ sdl_free_displaysurface(surface);
+ return sdl_create_displaysurface(width, height);
}
/* generic keyboard conversion */
#include "sdl_keysym.h"
-#include "keymaps.c"
static kbd_layout_t *kbd_layout = NULL;
if (keysym == 92 && ev->keysym.scancode == 133) {
keysym = 0xa5;
}
- return keysym2scancode(kbd_layout, keysym);
+ return keysym2scancode(kbd_layout, keysym) & SCANCODE_KEYMASK;
}
/* specific keyboard conversions from scan codes */
#else
+#if defined(SDL_VIDEO_DRIVER_X11)
+#include <X11/XKBlib.h>
+
+static int check_for_evdev(void)
+{
+ SDL_SysWMinfo info;
+ XkbDescPtr desc = NULL;
+ int has_evdev = 0;
+ char *keycodes = NULL;
+
+ SDL_VERSION(&info.version);
+ if (!SDL_GetWMInfo(&info)) {
+ return 0;
+ }
+ desc = XkbGetKeyboard(info.info.x11.display,
+ XkbGBN_AllComponentsMask,
+ XkbUseCoreKbd);
+ if (desc && desc->names) {
+ keycodes = XGetAtomName(info.info.x11.display, desc->names->keycodes);
+ if (keycodes == NULL) {
+ fprintf(stderr, "could not lookup keycode name\n");
+ } else if (strstart(keycodes, "evdev", NULL)) {
+ has_evdev = 1;
+ } else if (!strstart(keycodes, "xfree86", NULL)) {
+ fprintf(stderr, "unknown keycodes `%s', please report to "
+ }
+ }
+
+ if (desc) {
+ XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
+ }
+ if (keycodes) {
+ XFree(keycodes);
+ }
+ return has_evdev;
+}
+#else
+static int check_for_evdev(void)
+{
+ return 0;
+}
+#endif
+
static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
{
int keycode;
+ static int has_evdev = -1;
+
+ if (has_evdev == -1)
+ has_evdev = check_for_evdev();
keycode = ev->keysym.scancode;
keycode = 0;
} else if (keycode < 97) {
keycode -= 8; /* just an offset */
- } else if (keycode < 212) {
+ } else if (keycode < 158) {
/* use conversion table */
- keycode = _translate_keycode(keycode - 97);
+ if (has_evdev)
+ keycode = translate_evdev_keycode(keycode - 97);
+ else
+ keycode = translate_xfree86_keycode(keycode - 97);
+ } else if (keycode == 208) { /* Hiragana_Katakana */
+ keycode = 0x70;
+ } else if (keycode == 211) { /* backslash */
+ keycode = 0x73;
} else {
keycode = 0;
}
int i;
for(i = 0; i < 256; i++) {
if (modifiers_state[i]) {
- if (i & 0x80)
- kbd_put_keycode(0xe0);
- kbd_put_keycode(i | 0x80);
+ if (i & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
+ kbd_put_keycode(i | SCANCODE_UP);
modifiers_state[i] = 0;
}
}
/* specific case */
v = 0;
if (ev->type == SDL_KEYUP)
- v |= 0x80;
+ v |= SCANCODE_UP;
kbd_put_keycode(0xe1);
kbd_put_keycode(0x1d | v);
kbd_put_keycode(0x45 | v);
case 0x3a: /* caps lock */
/* SDL does not send the key up event, so we generate it */
kbd_put_keycode(keycode);
- kbd_put_keycode(keycode | 0x80);
+ kbd_put_keycode(keycode | SCANCODE_UP);
return;
}
/* now send the key code */
- if (keycode & 0x80)
- kbd_put_keycode(0xe0);
+ if (keycode & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
if (ev->type == SDL_KEYUP)
- kbd_put_keycode(keycode | 0x80);
+ kbd_put_keycode(keycode | SCANCODE_UP);
else
- kbd_put_keycode(keycode & 0x7f);
+ kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
}
static void sdl_update_caption(void)
{
- char buf[1024];
+ char win_title[1024];
+ char icon_title[1024];
const char *status = "";
if (!vm_running)
status = " [Stopped]";
else if (gui_grab) {
- if (!alt_grab)
- status = " - Press Ctrl-Alt to exit grab";
+ if (alt_grab)
+ status = " - Press Ctrl-Alt-Shift to exit mouse grab";
+ else if (ctrl_grab)
+ status = " - Press Right-Ctrl to exit mouse grab";
else
- status = " - Press Ctrl-Alt-Shift to exit grab";
+ status = " - Press Ctrl-Alt to exit mouse grab";
}
- if (qemu_name)
- snprintf(buf, sizeof(buf), "QEMU (%s)%s", qemu_name, status);
- else
- snprintf(buf, sizeof(buf), "QEMU%s", status);
+ if (qemu_name) {
+ snprintf(win_title, sizeof(win_title), "QEMU (%s)%s", qemu_name, status);
+ snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
+ } else {
+ snprintf(win_title, sizeof(win_title), "QEMU%s", status);
+ snprintf(icon_title, sizeof(icon_title), "QEMU");
+ }
- SDL_WM_SetCaption(buf, "QEMU");
+ SDL_WM_SetCaption(win_title, icon_title);
}
static void sdl_hide_cursor(void)
{
if (guest_cursor) {
SDL_SetCursor(guest_sprite);
- SDL_WarpMouse(guest_x, guest_y);
+ if (!kbd_mouse_is_absolute() && !absolute_enabled)
+ SDL_WarpMouse(guest_x, guest_y);
} else
sdl_hide_cursor();
- SDL_WM_GrabInput(SDL_GRAB_ON);
- gui_grab = 1;
- sdl_update_caption();
+
+ if (SDL_WM_GrabInput(SDL_GRAB_ON) == SDL_GRAB_ON) {
+ gui_grab = 1;
+ sdl_update_caption();
+ } else
+ sdl_show_cursor();
}
static void sdl_grab_end(void)
sdl_update_caption();
}
+static void sdl_mouse_mode_change(Notifier *notify)
+{
+ if (kbd_mouse_is_absolute()) {
+ if (!absolute_enabled) {
+ sdl_hide_cursor();
+ if (gui_grab) {
+ sdl_grab_end();
+ }
+ absolute_enabled = 1;
+ }
+ } else if (absolute_enabled) {
+ sdl_show_cursor();
+ absolute_enabled = 0;
+ }
+}
+
static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state)
{
int buttons;
buttons |= MOUSE_EVENT_MBUTTON;
if (kbd_mouse_is_absolute()) {
- if (!absolute_enabled) {
- sdl_hide_cursor();
- if (gui_grab) {
- sdl_grab_end();
- }
- absolute_enabled = 1;
- }
-
dx = x * 0x7FFF / (width - 1);
dy = y * 0x7FFF / (height - 1);
- } else if (absolute_enabled) {
- sdl_show_cursor();
- absolute_enabled = 0;
} else if (guest_cursor) {
x -= guest_x;
y -= guest_y;
static void toggle_full_screen(DisplayState *ds)
{
gui_fullscreen = !gui_fullscreen;
- sdl_resize(ds, screen->w, screen->h);
+ do_sdl_resize(real_screen->w, real_screen->h, real_screen->format->BitsPerPixel);
if (gui_fullscreen) {
+ scaling_active = 0;
gui_saved_grab = gui_grab;
sdl_grab_start();
} else {
while (SDL_PollEvent(ev)) {
switch (ev->type) {
case SDL_VIDEOEXPOSE:
- sdl_update(ds, 0, 0, screen->w, screen->h);
+ sdl_update(ds, 0, 0, real_screen->w, real_screen->h);
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
if (ev->type == SDL_KEYDOWN) {
- if (!alt_grab) {
- mod_state = (SDL_GetModState() & gui_grab_code) ==
- gui_grab_code;
- } else {
+ if (alt_grab) {
mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
(gui_grab_code | KMOD_LSHIFT);
+ } else if (ctrl_grab) {
+ mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
+ } else {
+ mod_state = (SDL_GetModState() & gui_grab_code) ==
+ gui_grab_code;
}
gui_key_modifier_pressed = mod_state;
if (gui_key_modifier_pressed) {
toggle_full_screen(ds);
gui_keysym = 1;
break;
+ case 0x16: /* 'u' key on US keyboard */
+ scaling_active = 0;
+ sdl_resize(ds);
+ vga_hw_invalidate();
+ vga_hw_update();
+ break;
case 0x02 ... 0x0a: /* '1' to '9' keys */
/* Reset the modifiers sent to the current console */
reset_keys();
sdl_process_key(&ev->key);
break;
case SDL_QUIT:
- if (!no_quit) {
+ if (!no_quit)
qemu_system_shutdown_request();
- vm_start(); /* In case we're paused */
- }
break;
case SDL_MOUSEMOTION:
if (gui_grab || kbd_mouse_is_absolute() ||
if (ev->active.state & SDL_APPACTIVE) {
if (ev->active.gain) {
/* Back to default interval */
- ds->gui_timer_interval = 0;
+ dcl->gui_timer_interval = 0;
+ dcl->idle = 0;
} else {
/* Sleeping interval */
- ds->gui_timer_interval = 500;
+ dcl->gui_timer_interval = 500;
+ dcl->idle = 1;
}
}
break;
+ case SDL_VIDEORESIZE:
+ {
+ SDL_ResizeEvent *rev = &ev->resize;
+ int bpp = real_screen->format->BitsPerPixel;
+ if (bpp != 16 && bpp != 32)
+ bpp = 32;
+ do_sdl_resize(rev->w, rev->h, bpp);
+ scaling_active = 1;
+ if (!is_buffer_shared(ds->surface)) {
+ ds->surface = qemu_resize_displaysurface(ds, ds_get_width(ds), ds_get_height(ds));
+ dpy_resize(ds);
+ }
+ vga_hw_invalidate();
+ vga_hw_update();
+ break;
+ }
default:
break;
}
static void sdl_fill(DisplayState *ds, int x, int y, int w, int h, uint32_t c)
{
SDL_Rect dst = { x, y, w, h };
- SDL_FillRect(screen, &dst, c);
+ SDL_FillRect(real_screen, &dst, c);
}
static void sdl_mouse_warp(int x, int y, int on)
sdl_show_cursor();
if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
SDL_SetCursor(guest_sprite);
- SDL_WarpMouse(x, y);
+ if (!kbd_mouse_is_absolute() && !absolute_enabled)
+ SDL_WarpMouse(x, y);
}
} else if (gui_grab)
sdl_hide_cursor();
guest_x = x, guest_y = y;
}
-static void sdl_mouse_define(int width, int height, int bpp,
- int hot_x, int hot_y,
- uint8_t *image, uint8_t *mask)
+static void sdl_mouse_define(QEMUCursor *c)
{
- uint8_t sprite[256], *line;
- int x, y, dst, bypl, src = 0;
+ uint8_t *image, *mask;
+ int bpl;
+
if (guest_sprite)
SDL_FreeCursor(guest_sprite);
- memset(sprite, 0, 256);
- bypl = ((width * bpp + 31) >> 5) << 2;
- for (y = 0, dst = 0; y < height; y ++, image += bypl) {
- line = image;
- for (x = 0; x < width; x ++, dst ++) {
- switch (bpp) {
- case 24:
- src = *(line ++); src |= *(line ++); src |= *(line ++);
- break;
- case 16:
- case 15:
- src = *(line ++); src |= *(line ++);
- break;
- case 8:
- src = *(line ++);
- break;
- case 4:
- src = 0xf & (line[x >> 1] >> ((x & 1)) << 2);
- break;
- case 2:
- src = 3 & (line[x >> 2] >> ((x & 3)) << 1);
- break;
- case 1:
- src = 1 & (line[x >> 3] >> (x & 7));
- break;
- }
- if (!src)
- sprite[dst >> 3] |= (1 << (~dst & 7)) & mask[dst >> 3];
- }
- }
- guest_sprite = SDL_CreateCursor(sprite, mask, width, height, hot_x, hot_y);
+ bpl = cursor_get_mono_bpl(c);
+ image = qemu_mallocz(bpl * c->height);
+ mask = qemu_mallocz(bpl * c->height);
+ cursor_get_mono_image(c, 0x000000, image);
+ cursor_get_mono_mask(c, 0, mask);
+ guest_sprite = SDL_CreateCursor(image, mask, c->width, c->height,
+ c->hot_x, c->hot_y);
+ qemu_free(image);
+ qemu_free(mask);
if (guest_cursor &&
(gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
{
if (guest_sprite)
SDL_FreeCursor(guest_sprite);
- SDL_Quit();
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
}
void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
{
int flags;
uint8_t data = 0;
+ DisplayAllocator *da;
+ const SDL_VideoInfo *vi;
#if defined(__APPLE__)
/* always use generic keymaps */
keyboard_layout = "en-us";
#endif
if(keyboard_layout) {
- kbd_layout = init_keyboard_layout(keyboard_layout);
+ kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
if (!kbd_layout)
exit(1);
}
if (no_frame)
gui_noframe = 1;
+ if (!full_screen) {
+ setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 0);
+ }
+
flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
if (SDL_Init (flags)) {
- fprintf(stderr, "Could not initialize SDL - exiting\n");
+ fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
+ SDL_GetError());
exit(1);
}
-#ifndef _WIN32
- /* NOTE: we still want Ctrl-C to work, so we undo the SDL redirections */
- signal(SIGINT, SIG_DFL);
- signal(SIGQUIT, SIG_DFL);
-#endif
-
- ds->dpy_update = sdl_update;
- ds->dpy_resize = sdl_resize;
- ds->dpy_refresh = sdl_refresh;
- ds->dpy_fill = sdl_fill;
+ vi = SDL_GetVideoInfo();
+ host_format = *(vi->vfmt);
+
+ dcl = qemu_mallocz(sizeof(DisplayChangeListener));
+ dcl->dpy_update = sdl_update;
+ dcl->dpy_resize = sdl_resize;
+ dcl->dpy_refresh = sdl_refresh;
+ dcl->dpy_setdata = sdl_setdata;
+ dcl->dpy_fill = sdl_fill;
ds->mouse_set = sdl_mouse_warp;
ds->cursor_define = sdl_mouse_define;
+ register_displaychangelistener(ds, dcl);
+
+ da = qemu_mallocz(sizeof(DisplayAllocator));
+ da->create_displaysurface = sdl_create_displaysurface;
+ da->resize_displaysurface = sdl_resize_displaysurface;
+ da->free_displaysurface = sdl_free_displaysurface;
+ if (register_displayallocator(ds, da) == da) {
+ dpy_resize(ds);
+ }
+
+ mouse_mode_notifier.notify = sdl_mouse_mode_change;
+ qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
- sdl_resize(ds, 640, 400);
sdl_update_caption();
SDL_EnableKeyRepeat(250, 50);
gui_grab = 0;