diff --git a/input/input_driver.c b/input/input_driver.c index 8f552408cd00..96da8afbead3 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -1635,13 +1635,14 @@ void input_keyboard_line_free(input_driver_state_t *input_st) input_keyboard_line_t *kb_line = (input_keyboard_line_t*)&input_st->keyboard_line; if (kb_line->buffer) free(kb_line->buffer); - kb_line->buffer = NULL; - kb_line->ptr = 0; - kb_line->size = 0; - kb_line->capacity = 0; - kb_line->cb = NULL; - kb_line->userdata = NULL; - kb_line->enabled = false; + kb_line->buffer = NULL; + kb_line->ptr = 0; + kb_line->size = 0; + kb_line->capacity = 0; + kb_line->cb = NULL; + kb_line->userdata = NULL; + kb_line->enabled = false; + input_st->osk_textbox_focus = false; } const char **input_keyboard_start_line( @@ -1649,13 +1650,14 @@ const char **input_keyboard_start_line( struct input_keyboard_line *kb_line, input_keyboard_line_complete_t cb) { - kb_line->buffer = NULL; - kb_line->ptr = 0; - kb_line->size = 0; - kb_line->capacity = 0; - kb_line->cb = cb; - kb_line->userdata = userdata; - kb_line->enabled = true; + kb_line->buffer = NULL; + kb_line->ptr = 0; + kb_line->size = 0; + kb_line->capacity = 0; + kb_line->cb = cb; + kb_line->userdata = userdata; + kb_line->enabled = true; + input_driver_st.osk_textbox_focus = false; return (const char**)&kb_line->buffer; } @@ -8244,13 +8246,15 @@ void input_driver_collect_system_input(input_driver_state_t *input_st, else if (display_kb && input && input->input_state) { /* OSK grid navigation from keyboard / TV remote D-pad, plus - * page switches and Escape to clear/close. Return confirms the - * highlighted OSK character (same as menu OK). */ + * page switches and Escape to clear/close. Return and LCTRL/RCTRL + * confirm the highlighted OSK character (same as menu OK) */ unsigned i; bool swap_ok_cancel_buttons = settings->bools.input_menu_swap_ok_cancel_buttons; unsigned ids[][2] = { {RETROK_RETURN, RETRO_DEVICE_ID_JOYPAD_A }, + {RETROK_LCTRL, RETRO_DEVICE_ID_JOYPAD_A }, + {RETROK_RCTRL, RETRO_DEVICE_ID_JOYPAD_A }, {RETROK_UP, RETRO_DEVICE_ID_JOYPAD_UP }, {RETROK_DOWN, RETRO_DEVICE_ID_JOYPAD_DOWN }, {RETROK_LEFT, RETRO_DEVICE_ID_JOYPAD_LEFT }, @@ -8265,6 +8269,16 @@ void input_driver_collect_system_input(input_driver_state_t *input_st, for (i = 0; i < ARRAY_SIZE(ids); i++) { + if ( swap_ok_cancel_buttons + && (ids[i][0] == RETROK_LCTRL || ids[i][0] == RETROK_RCTRL)) + ids[i][1] = RETRO_DEVICE_ID_JOYPAD_B; + + if ( input_st->osk_textbox_focus + && ids[i][1] != RETRO_DEVICE_ID_JOYPAD_L + && ids[i][1] != RETRO_DEVICE_ID_JOYPAD_R + && ids[i][1] != RETRO_DEVICE_ID_JOYPAD_SELECT) + continue; + if (ids[i][0] && input->input_state( input_st->current_data, joypad, @@ -8500,42 +8514,51 @@ void input_keyboard_event(bool down, unsigned code, if (!down) return; - switch (code) + if (code == RETROK_TAB) { - case RETROK_LEFT: - if (line->ptr && line->buffer) - { - line->ptr--; - while (line->ptr && IS_UTF8_CONTINUATION(line->buffer[line->ptr])) - line->ptr--; + input_st->osk_textbox_focus = !input_st->osk_textbox_focus; + return; + } - if (mod & RETROKMOD_CTRL) - while (line->ptr && - (ISSPACE(line->buffer[line->ptr]) || !ISSPACE(line->buffer[line->ptr - 1]))) + if (input_st->osk_textbox_focus) + { + switch (code) + { + case RETROK_LEFT: + if (line->ptr && line->buffer) + { + line->ptr--; + while (line->ptr && IS_UTF8_CONTINUATION(line->buffer[line->ptr])) line->ptr--; - } - return; - case RETROK_RIGHT: - if (line->buffer && line->ptr < line->size) - { - line->ptr++; - while (line->ptr < line->size && IS_UTF8_CONTINUATION(line->buffer[line->ptr])) - line->ptr++; - if (mod & RETROKMOD_CTRL) - while (line->ptr < line->size && - (ISSPACE(line->buffer[line->ptr]) || !ISSPACE(line->buffer[line->ptr - 1]))) + if (mod & RETROKMOD_CTRL) + while (line->ptr && + (ISSPACE(line->buffer[line->ptr]) || !ISSPACE(line->buffer[line->ptr - 1]))) + line->ptr--; + } + return; + case RETROK_RIGHT: + if (line->buffer && line->ptr < line->size) + { + line->ptr++; + while (line->ptr < line->size && IS_UTF8_CONTINUATION(line->buffer[line->ptr])) line->ptr++; - } - return; - case RETROK_UP: - line->ptr = 0; - return; - case RETROK_DOWN: - line->ptr = line->size; - return; - default: - break; + + if (mod & RETROKMOD_CTRL) + while (line->ptr < line->size && + (ISSPACE(line->buffer[line->ptr]) || !ISSPACE(line->buffer[line->ptr - 1]))) + line->ptr++; + } + return; + case RETROK_UP: + line->ptr = 0; + return; + case RETROK_DOWN: + line->ptr = line->size; + return; + default: + break; + } } if (!input_keyboard_line_event(input_st, line, character)) diff --git a/input/input_driver.h b/input/input_driver.h index c3890a336cdd..dc3b0000f38d 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -623,6 +623,7 @@ typedef struct #endif #endif int osk_ptr; + bool osk_textbox_focus; turbo_buttons_t turbo_btns; /* int32_t alignment */ hold_buttons_t hold_btns; /* int32_t alignment */ diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 88edafb38ac7..4f0ca27e134b 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -721,6 +721,10 @@ typedef struct materialui_handle /* Scrollbar parameters */ materialui_scrollbar_t scrollbar; /* int alignment */ int cursor_size; + int osk_textbox_x; + int osk_textbox_y; + int osk_textbox_w; + int osk_textbox_h; /* Cached system bar data */ materialui_sys_bar_cache_t sys_bar_cache; /* int alignment */ float last_scale_factor; @@ -2831,6 +2835,7 @@ static void materialui_render_messagebox( int y_centre, const char *msg, bool draw_caret, + bool draw_focus, math_matrix_4x4 *mymat) { int i; @@ -2976,6 +2981,14 @@ static void materialui_render_messagebox( if (confirm_dialog) slice_h += (mui->font_data.list.line_height + mui->margin) * 2; + if (draw_focus) + { + mui->osk_textbox_x = slice_x; + mui->osk_textbox_y = slice_y; + mui->osk_textbox_w = slice_w; + mui->osk_textbox_h = slice_h; + } + /* Draw message box background */ gfx_display_set_alpha( mui->colors.surface_background, mui->transition_alpha); @@ -2992,6 +3005,36 @@ static void materialui_render_messagebox( video_height, mui->colors.surface_background, NULL); + + if (draw_focus && input_state_get_ptr()->osk_textbox_focus) + { + int cursor_s = ((int)(mui->last_scale_factor * 4.0f) < 3) + ? 3 : (int)(mui->last_scale_factor * 4.0f); + unsigned j; + + for (j = 0; j < 4; j++) + { + int cursor_x = (j == 3) ? slice_x + slice_w - cursor_s : slice_x; + int cursor_y = (j == 1) ? slice_y + slice_h - cursor_s : slice_y; + int cursor_w = (j < 2) ? slice_w : cursor_s; + int cursor_h = (j < 2) ? cursor_s : slice_h; + + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + cursor_x, + cursor_y, + cursor_w, + cursor_h, + video_width, + video_height, + mui->colors.list_icon, + NULL); + } + } + /* Print each line of the message */ for (i = 0; i < line_count; i++) { @@ -3171,6 +3214,26 @@ static void materialui_render_messagebox( } } +static bool materialui_osk_pointer_over_textbox( + void *data, + int x, + int y, + unsigned width, + unsigned height) +{ + materialui_handle_t *mui = (materialui_handle_t*)data; + + if ( mui + && menu_input_dialog_get_display_kb() + && x > mui->osk_textbox_x + && x < mui->osk_textbox_x + mui->osk_textbox_w + && y > mui->osk_textbox_y + && y < mui->osk_textbox_y + mui->osk_textbox_h) + return true; + + return false; +} + /* Initialises scrollbar parameters (width/height) */ static void materialui_scrollbar_init( materialui_handle_t* mui, @@ -8533,7 +8596,7 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) materialui_render_messagebox(mui, p_disp, userdata, video_width, video_height, - video_height / 4, msg, true, + video_height / 4, msg, true, true, &mymat); /* Draw onscreen keyboard */ @@ -8546,7 +8609,7 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) tex_list[MUI_TEXTURE_KEY_HOVER], mui->font_data.list.font, input_st->osk_grid, - input_st->osk_ptr, + input_st->osk_textbox_focus ? 44 : input_st->osk_ptr, 0xFFFFFFFF); } @@ -8576,7 +8639,7 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) materialui_render_messagebox(mui, p_disp, userdata, video_width, video_height, - video_height / 2, mui->msgbox, false, + video_height / 2, mui->msgbox, false, false, &mymat); mui->msgbox[0] = '\0'; @@ -12713,6 +12776,7 @@ menu_ctx_driver_t menu_ctx_mui = { materialui_refresh_thumbnail_image, NULL, gfx_display_osk_ptr_at_pos, + materialui_osk_pointer_over_textbox, materialui_update_savestate_thumbnail_path, materialui_update_savestate_thumbnail_image, materialui_pointer_down, diff --git a/menu/drivers/ozone.c b/menu/drivers/ozone.c index d1af9c1d3af0..d57187b0e17a 100644 --- a/menu/drivers/ozone.c +++ b/menu/drivers/ozone.c @@ -7356,6 +7356,42 @@ OZONE_NOINLINE static void ozone_draw_osk( text_color = ozone_theme_light.text_sublabel_rgba; } + if (input_st->osk_textbox_focus) + { + float cursor_color[16]; + int cursor_s = ozone->dimensions.spacer_5px; + int cursor_x = margin; + int cursor_y = margin; + int cursor_w = video_width - (margin * 2); + int cursor_h = bottom_end - (margin * 2); + unsigned j; + + memcpy(cursor_color, ozone->theme_dynamic.cursor_border, sizeof(cursor_color)); + gfx_display_set_alpha(cursor_color, 1.0f); + + for (j = 0; j < 4; j++) + { + int rect_x = (j == 3) ? cursor_x + cursor_w - cursor_s : cursor_x; + int rect_y = (j == 1) ? cursor_y + cursor_h - cursor_s : cursor_y; + int rect_w = (j < 2) ? cursor_w : cursor_s; + int rect_h = (j < 2) ? cursor_s : cursor_h; + + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + rect_x, + rect_y, + rect_w, + rect_h, + video_width, + video_height, + cursor_color, + NULL); + } + } + if (!draw_placeholder && input_st->keyboard_line.buffer) { char cursor_message[2048]; @@ -7471,11 +7507,37 @@ OZONE_NOINLINE static void ozone_draw_osk( : ozone->textures[OZONE_TEXTURE_CURSOR_BORDER], ozone->fonts.entries_label.font, input_st->osk_grid, - input_st->osk_ptr, + input_st->osk_textbox_focus ? 44 : input_st->osk_ptr, ozone->theme->text_rgba); } } +static bool ozone_osk_pointer_over_textbox( + void *data, + int x, + int y, + unsigned width, + unsigned height) +{ + ozone_handle_t *ozone = (ozone_handle_t*)data; + + if (ozone && menu_input_dialog_get_display_kb()) + { + unsigned margin = 75 * ozone->last_scale_factor; + unsigned bottom_end = height / 2; + + if ( width > (margin * 2) + && bottom_end > (margin * 2) + && (unsigned)x > margin + && (unsigned)x < width - margin + && (unsigned)y > margin + && (unsigned)y < bottom_end - margin) + return true; + } + + return false; +} + OZONE_NOINLINE static void ozone_draw_messagebox( ozone_handle_t *ozone, gfx_display_t *p_disp, @@ -13835,6 +13897,7 @@ menu_ctx_driver_t menu_ctx_ozone = { ozone_refresh_thumbnail_image, ozone_set_thumbnail_content, gfx_display_osk_ptr_at_pos, + ozone_osk_pointer_over_textbox, ozone_update_savestate_thumbnail_path, ozone_update_savestate_thumbnail_image, NULL, /* pointer_down */ diff --git a/menu/drivers/rgui.c b/menu/drivers/rgui.c index 8741d189598b..1ee4eed3efee 100644 --- a/menu/drivers/rgui.c +++ b/menu/drivers/rgui.c @@ -5152,6 +5152,35 @@ static void rgui_render_messagebox( } } +static bool rgui_osk_pointer_over_textbox( + void *data, + int x, + int y, + unsigned width, + unsigned height) +{ + rgui_t *rgui = (rgui_t*)data; + + if (!rgui) + return false; + + { + gfx_display_t *p_disp = disp_get_ptr(); + unsigned key_width = rgui->font_width + 16; + unsigned key_height = rgui->font_height + 12; + unsigned keyboard_offset_y = 10 + 15 + (2 * rgui->font_height_stride); + unsigned osk_width = (key_width * OSK_CHARS_PER_LINE) + 20; + unsigned osk_height = keyboard_offset_y + (key_height * 4) + 10; + unsigned osk_x = (p_disp->framebuf_width - osk_width) / 2; + unsigned osk_y = (p_disp->framebuf_height - osk_height) / 2; + + return (unsigned)x > osk_x + 5 + && (unsigned)x < osk_x + osk_width - 5 + && (unsigned)y > osk_y + 5 + && (unsigned)y < osk_y + keyboard_offset_y - 10; + } +} + static int rgui_osk_ptr_at_pos( void *data, int x, @@ -5187,6 +5216,24 @@ static int rgui_osk_ptr_at_pos( unsigned osk_x = (fb_width - osk_width) / 2; unsigned osk_y = (fb_height - osk_height) / 2; + if ( (unsigned)x > osk_x + 5 + && (unsigned)x < osk_x + osk_width - 5 + && (unsigned)y > osk_y + keyboard_offset_y - 10 + && (unsigned)y < osk_y + osk_height - 5) + { + unsigned key_row = ((unsigned)y > osk_y + keyboard_offset_y) + ? ((unsigned)y - osk_y - keyboard_offset_y) / key_height : 0; + unsigned key_column = ((unsigned)x > osk_x + keyboard_offset_x) + ? ((unsigned)x - osk_x - keyboard_offset_x) / key_width : 0; + + if (key_row > 3) + key_row = 3; + if (key_column > OSK_CHARS_PER_LINE - 1) + key_column = OSK_CHARS_PER_LINE - 1; + + return (int)(key_row * OSK_CHARS_PER_LINE + key_column); + } + for (key_index = 0; key_index < 44; key_index++) { unsigned key_row = (unsigned)(key_index / OSK_CHARS_PER_LINE); @@ -5429,6 +5476,44 @@ RGUI_NOINLINE static void rgui_render_osk( last_cursor_time = current_time; } + if (input_st->osk_textbox_focus) + { + unsigned input_ptr_x = osk_x + 5; + unsigned input_ptr_y = osk_y + 5; + unsigned input_ptr_width = osk_width - 10; + unsigned input_ptr_height = keyboard_offset_y - 15; + unsigned j; + + for (j = 0; j < 4; j++) + { + unsigned rect_x = (j == 3) ? input_ptr_x + input_ptr_width - 1 : input_ptr_x; + unsigned rect_y = (j == 1) ? input_ptr_y + input_ptr_height - 1 : input_ptr_y; + unsigned rect_w = (j < 2) ? input_ptr_width : 1; + unsigned rect_h = (j < 2) ? 1 : input_ptr_height; + + if (rgui->flags & RGUI_FLAG_SHADOW_ENABLE) + rgui_color_rect( + frame_buf_data, + fb_width, + fb_height, + rect_x + 1, + rect_y + 1, + rect_w, + rect_h, + rgui->colors.shadow_color); + + rgui_color_rect( + frame_buf_data, + fb_width, + fb_height, + rect_x, + rect_y, + rect_w, + rect_h, + rgui->colors.hover_color); + } + } + if (!(((current_time - last_cursor_time) / 500000) & 1)) rgui_blit_symbol(rgui, fb_width, text_cursor_x, input_str_y, RGUI_SYMBOL_TEXT_CURSOR, rgui->colors.normal_color, rgui->colors.shadow_color); @@ -5487,7 +5572,7 @@ RGUI_NOINLINE static void rgui_render_osk( rgui->colors.normal_color, rgui->colors.shadow_color); /* Draw selection pointer */ - if (key_index == osk_ptr) + if (!input_st->osk_textbox_focus && (key_index == osk_ptr)) { unsigned osk_ptr_x = osk_x + keyboard_offset_x + ptr_offset_x + (key_column * key_width); unsigned osk_ptr_y = osk_y + keyboard_offset_y + ptr_offset_y + (key_row * key_height); @@ -8979,6 +9064,7 @@ menu_ctx_driver_t menu_ctx_rgui = { rgui_refresh_thumbnail_image, NULL, /* set_thumbnail_content */ rgui_osk_ptr_at_pos, + rgui_osk_pointer_over_textbox, rgui_update_savestate_thumbnail_path, rgui_update_savestate_thumbnail_image, NULL, /* pointer_down */ diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index fa126ab67de1..f64105bf50e3 100644 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -436,6 +436,10 @@ typedef struct xmb_handle int old_depth; int icon_size; int cursor_size; + int osk_textbox_x; + int osk_textbox_y; + int osk_textbox_w; + int osk_textbox_h; int wideglyph_width; unsigned categories_active_idx; @@ -1394,6 +1398,44 @@ XMB_NOINLINE static void xmb_render_messagebox_internal( if (confirm_dialog) slice_h += (line_height * 2); + if (input_dialog_display_kb) + { + xmb->osk_textbox_x = slice_x; + xmb->osk_textbox_y = slice_y; + xmb->osk_textbox_w = slice_w; + xmb->osk_textbox_h = slice_h; + } + + if (input_dialog_display_kb && input_state_get_ptr()->osk_textbox_focus && line_count > 1) + { + int cursor_offset = (xmb->margins_dialog + (xmb->margins_slice * 2)) / 3; + + if (dispctx && dispctx->blend_begin) + dispctx->blend_begin(userdata); + + gfx_display_draw_texture_slice( + p_disp, + userdata, + video_width, + video_height, + slice_x - cursor_offset, + slice_y - cursor_offset, + 256, + 256, + slice_w + (cursor_offset * 2), + slice_h + (cursor_offset * 2), + video_width, + video_height, + NULL, + xmb->margins_slice, + xmb->last_scale_factor, + xmb->textures.list[XMB_TEXTURE_KEY_HOVER], + mymat); + + if (dispctx && dispctx->blend_end) + dispctx->blend_end(userdata); + } + if (!xmb->assets_missing) { if (dispctx && dispctx->blend_begin) @@ -1494,7 +1536,7 @@ XMB_NOINLINE static void xmb_render_messagebox_internal( xmb->textures.list[XMB_TEXTURE_KEY_HOVER], xmb->font, input_st->osk_grid, - input_st->osk_ptr, + input_st->osk_textbox_focus ? 44 : input_st->osk_ptr, 0xffffffff); } @@ -1669,6 +1711,26 @@ XMB_NOINLINE static void xmb_render_messagebox_internal( } } +static bool xmb_osk_pointer_over_textbox( + void *data, + int x, + int y, + unsigned width, + unsigned height) +{ + xmb_handle_t *xmb = (xmb_handle_t*)data; + + if ( xmb + && menu_input_dialog_get_display_kb() + && x > xmb->osk_textbox_x + && x < xmb->osk_textbox_x + xmb->osk_textbox_w + && y > xmb->osk_textbox_y + && y < xmb->osk_textbox_y + xmb->osk_textbox_h) + return true; + + return false; +} + #ifdef HAVE_LIBRETRODB static bool xmb_is_main_menu_explore(void) { @@ -11121,6 +11183,7 @@ menu_ctx_driver_t menu_ctx_xmb = { xmb_refresh_thumbnail_image, xmb_set_thumbnail_content, gfx_display_osk_ptr_at_pos, + xmb_osk_pointer_over_textbox, xmb_update_savestate_thumbnail_path, xmb_update_savestate_thumbnail_image, xmb_pointer_down, diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 651d1020cb11..fd799268c093 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -335,6 +335,7 @@ static menu_ctx_driver_t menu_ctx_null = { NULL, /* refresh_thumbnail_image */ NULL, /* set_thumbnail_content */ NULL, /* osk_ptr_at_pos */ + NULL, /* osk_pointer_over_textbox */ NULL, /* update_savestate_thumbnail_path */ NULL, /* update_savestate_thumbnail_image */ NULL, /* pointer_down */ @@ -6236,20 +6237,33 @@ MENU_NOINLINE static int menu_input_post_iterate( * has remained stationary */ if (!(menu_input->pointer.flags & MENU_INP_PTR_FLG_DRAGGED)) { - menu_driver_ctl(RARCH_MENU_CTL_OSK_PTR_AT_POS, &point); - if (point.retcode > -1) + if ( menu_st->driver_ctx + && menu_st->driver_ctx->osk_pointer_over_textbox + && menu_st->driver_ctx->osk_pointer_over_textbox( + menu_st->userdata, x, y, video_st->width, video_st->height)) + input_st->osk_textbox_focus = true; + else { - bool show_osk_symbols = input_event_osk_show_symbol_pages(menu_st->driver_data); - input_st->osk_ptr = point.retcode; - input_event_osk_append( - &input_st->keyboard_line, - &input_st->osk_idx, - &input_st->osk_last_codepoint, - &input_st->osk_last_codepoint_len, - point.retcode, - show_osk_symbols, - input_st->osk_grid[input_st->osk_ptr], - strlen(input_st->osk_grid[input_st->osk_ptr])); + menu_driver_ctl(RARCH_MENU_CTL_OSK_PTR_AT_POS, &point); + if (point.retcode > -1) + { + bool textbox_focus = input_st->osk_textbox_focus; + input_st->osk_ptr = point.retcode; + input_st->osk_textbox_focus = false; + if (!textbox_focus) + { + bool show_osk_symbols = input_event_osk_show_symbol_pages(menu_st->driver_data); + input_event_osk_append( + &input_st->keyboard_line, + &input_st->osk_idx, + &input_st->osk_last_codepoint, + &input_st->osk_last_codepoint_len, + point.retcode, + show_osk_symbols, + input_st->osk_grid[input_st->osk_ptr], + strlen(input_st->osk_grid[input_st->osk_ptr])); + } + } } } #ifdef HAVE_MIST diff --git a/menu/menu_driver.h b/menu/menu_driver.h index b7774ecc8db0..36215943881a 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -406,6 +406,7 @@ typedef struct menu_ctx_driver void (*refresh_thumbnail_image)(void *data, size_t i); void (*set_thumbnail_content)(void *data, const char *s); int (*osk_ptr_at_pos)(void *data, int x, int y, unsigned width, unsigned height); + bool (*osk_pointer_over_textbox)(void *data, int x, int y, unsigned width, unsigned height); void (*update_savestate_thumbnail_path)(void *data, unsigned i); void (*update_savestate_thumbnail_image)(void *data); int (*pointer_down)(void *data, unsigned x, unsigned y, unsigned ptr,