mirror of
https://github.com/kichikuou/xsystem35-sdl2.git
synced 2026-09-22 22:48:08 +03:00
Use the SDL popup menu on Android
The popup menu opens with a three-finger tap. Add touch support to the modal framework: a tap is replayed as a multi-frame mouse hover->press->release so microui registers the click, and the press is held so sliders can be dragged. Fix a microui command-buffer alignment bug that crashed on 32-bit ARM.
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
||||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
- Replaced the GTK-based popup menu and dialogs with a pure-SDL implementation.
|
||||
- Replaced the GTK-based popup menu and dialogs with a pure-SDL implementation. On Android, the popup menu opens with a three-finger tap.
|
||||
|
||||
## 2.18.0 - 2026-06-23
|
||||
- Added support for the intro demo of Daiakuji.
|
||||
|
||||
@@ -47,6 +47,7 @@ file(COPY_FILE ${PROJECT_ROOT_DIR}/COPYING ${ASSETS_DIR}/licenses/xsystem35)
|
||||
file(COPY_FILE ${PROJECT_ROOT_DIR}/licenses/MTLc3m.txt ${ASSETS_DIR}/licenses/MTLc3m)
|
||||
file(COPY_FILE ${PROJECT_ROOT_DIR}/licenses/mincho.txt ${ASSETS_DIR}/licenses/mincho)
|
||||
file(COPY_FILE ${PROJECT_ROOT_DIR}/licenses/nanojpeg.txt ${ASSETS_DIR}/licenses/nanojpeg)
|
||||
file(COPY_FILE ${PROJECT_ROOT_DIR}/licenses/microui.txt ${ASSETS_DIR}/licenses/microui)
|
||||
file(COPY_FILE ${sdl_SOURCE_DIR}/LICENSE.txt ${ASSETS_DIR}/licenses/SDL)
|
||||
file(COPY_FILE ${sdl_ttf_SOURCE_DIR}/LICENSE.txt ${ASSETS_DIR}/licenses/SDL_ttf)
|
||||
file(COPY_FILE ${sdl_ttf_SOURCE_DIR}/external/freetype/docs/GPLv2.TXT ${ASSETS_DIR}/licenses/freetype)
|
||||
|
||||
@@ -14,6 +14,7 @@ class LicensesMenuActivity : Activity() {
|
||||
Entry("SDL_mixer", "SDL_mixer", "https://github.com/libsdl-org/SDL_mixer"),
|
||||
Entry("SDL_ttf", "SDL_ttf", "https://github.com/libsdl-org/SDL_ttf"),
|
||||
Entry("NanoJPEG", "nanojpeg", "https://keyj.emphy.de/nanojpeg/"),
|
||||
Entry("microui", "microui", "https://github.com/rxi/microui"),
|
||||
Entry("FreeType", "freetype", "https://freetype.org/"),
|
||||
Entry("HarfBuzz", "harfbuzz", "https://harfbuzz.github.io/"),
|
||||
Entry("MotoyaLCedar W3 mono", "MTLc3m", "https://github.com/aosp-mirror/platform_frameworks_base/tree/lollipop-release/data/fonts"),
|
||||
|
||||
@@ -121,8 +121,6 @@ endif()
|
||||
|
||||
if (EMSCRIPTEN)
|
||||
target_sources(xsystem35 PRIVATE menu_emscripten.c)
|
||||
elseif (ANDROID)
|
||||
target_sources(xsystem35 PRIVATE menu_android.c)
|
||||
else ()
|
||||
target_sources(xsystem35 PRIVATE
|
||||
menu_sdl.c modal.c microui/microui.c volume.c)
|
||||
|
||||
+3
-1
@@ -430,7 +430,9 @@ void event_handle_event(SDL_Event *e) {
|
||||
break;
|
||||
|
||||
case SDL_FINGERDOWN:
|
||||
if (SDL_GetNumTouchFingers(e->tfinger.touchId) >= 2) {
|
||||
if (SDL_GetNumTouchFingers(e->tfinger.touchId) >= 3) {
|
||||
menu_open();
|
||||
} else if (SDL_GetNumTouchFingers(e->tfinger.touchId) >= 2) {
|
||||
mouseb &= ~(1 << SDL_BUTTON_LEFT);
|
||||
mouseb |= 1 << SDL_BUTTON_RIGHT;
|
||||
RawKeyInfo[mouse_to_rawkey(SDL_BUTTON_LEFT)] = false;
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "system.h"
|
||||
#include "portab.h"
|
||||
#include "menu.h"
|
||||
#include "nact.h"
|
||||
|
||||
void menu_open(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
void menu_quitmenu_open(void) {
|
||||
const SDL_MessageBoxButtonData buttons[] = {
|
||||
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, "Quit" },
|
||||
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 0, "Cancel" },
|
||||
};
|
||||
const SDL_MessageBoxData messagebox_data = {
|
||||
.flags = SDL_MESSAGEBOX_INFORMATION,
|
||||
.window = NULL,
|
||||
.title = "Quit game?",
|
||||
.message = "Any unsaved progress will be lost.",
|
||||
.numbuttons = SDL_arraysize(buttons),
|
||||
.buttons = buttons,
|
||||
};
|
||||
int buttonid = 0;
|
||||
if (SDL_ShowMessageBox(&messagebox_data, &buttonid) < 0) {
|
||||
WARNING("error displaying message box");
|
||||
return;
|
||||
}
|
||||
if (buttonid == 1) {
|
||||
nact_quit(false);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_init(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
void menu_render_overlay(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
void menu_setSkipState(bool enabled, bool activated) {
|
||||
}
|
||||
+22
-4
@@ -51,19 +51,37 @@ struct popup_state {
|
||||
modal base;
|
||||
mu_Rect rect; // popup window rect from the most recent frame
|
||||
enum menu_action action; // what the user selected
|
||||
// A three-finger tap opens the menu, but SDL_GetNumTouchFingers() reports the
|
||||
// live finger count, so it already returns 3 while the first of the three
|
||||
// near-simultaneous FINGERDOWN events is being processed - the menu opens on
|
||||
// that first event. The other two FINGERDOWN events of the same gesture are
|
||||
// still queued and, once the menu is up, would be seen as taps that dismiss
|
||||
// it. Ignore touches until every finger has lifted, then arm tap-to-dismiss.
|
||||
bool touch_armed;
|
||||
};
|
||||
|
||||
// Message-skip state, kept in sync via menu_setSkipState().
|
||||
static bool skip_enabled = true;
|
||||
static bool skip_activated;
|
||||
|
||||
static bool point_in_rect(int x, int y, mu_Rect r) {
|
||||
return x >= r.x && x < r.x + r.w && y >= r.y && y < r.y + r.h;
|
||||
}
|
||||
|
||||
static bool menu_popup_handler(const SDL_Event *e, modal *modal) {
|
||||
struct popup_state *st = (struct popup_state *)modal;
|
||||
if (e->type == SDL_MOUSEBUTTONUP) {
|
||||
bool point_in_menu =
|
||||
e->button.x >= st->rect.x && e->button.x < st->rect.x + st->rect.w &&
|
||||
e->button.y >= st->rect.y && e->button.y < st->rect.y + st->rect.h;
|
||||
if (e->button.button == SDL_BUTTON_RIGHT || !point_in_menu)
|
||||
if (e->button.button == SDL_BUTTON_RIGHT ||
|
||||
!point_in_rect(e->button.x, e->button.y, st->rect))
|
||||
st->base.cancelled = true;
|
||||
} else if (e->type == SDL_FINGERUP) {
|
||||
// Arm tap-to-dismiss once the opening gesture's fingers are all lifted.
|
||||
if (SDL_GetNumTouchFingers(e->tfinger.touchId) == 0)
|
||||
st->touch_armed = true;
|
||||
} else if (e->type == SDL_FINGERDOWN && st->touch_armed) {
|
||||
// A tap outside the menu dismisses it.
|
||||
int x = e->tfinger.x * view_w, y = e->tfinger.y * view_h;
|
||||
if (!point_in_rect(x, y, st->rect))
|
||||
st->base.cancelled = true;
|
||||
}
|
||||
return modal_default_handler(e, modal);
|
||||
|
||||
@@ -426,6 +426,12 @@ void mu_input_text(mu_Context *ctx, const char *text) {
|
||||
|
||||
mu_Command* mu_push_command(mu_Context *ctx, int type, int size) {
|
||||
mu_Command *cmd = (mu_Command*) (ctx->command_list.items + ctx->command_list.idx);
|
||||
#ifdef __arm__
|
||||
/* xsystem35 change: round the entry size up to a pointer-aligned boundary.
|
||||
** Variable-length commands (text) would otherwise leave the following command
|
||||
** at an unaligned address, which faults on 32-bit ARM. */
|
||||
size = (size + (int) sizeof(void*) - 1) & ~((int) sizeof(void*) - 1);
|
||||
#endif
|
||||
expect(ctx->command_list.idx + size < MU_COMMANDLIST_SIZE);
|
||||
cmd->base.type = type;
|
||||
cmd->base.size = size;
|
||||
|
||||
+63
@@ -38,6 +38,22 @@
|
||||
static mu_Context *ctx;
|
||||
static modal *current_modal;
|
||||
|
||||
// Touch input. A tap is translated into a synthetic mouse hover->press->release
|
||||
// sequence spread across several frames, decoupled from the finger's real
|
||||
// timing. This is required because microui only registers a control as hovered
|
||||
// on a frame where no button is held, and the hovered window (hover_root) itself
|
||||
// updates a frame late; so the press must trail the finger landing by a couple
|
||||
// of frames or the tap never clicks. Holding the press also lets sliders drag.
|
||||
#define TOUCH_HOVER_FRAMES 2
|
||||
static enum {
|
||||
TOUCH_NONE,
|
||||
TOUCH_HOVER, // finger down; letting microui settle hover before pressing
|
||||
TOUCH_PRESS, // mouse button held (a drag tracks the finger in this phase)
|
||||
} touch_phase;
|
||||
static int touch_hover_frames; // frames left to dwell in TOUCH_HOVER
|
||||
static bool touch_release_after_press; // finger lifted early; release once pressed
|
||||
static int touch_x, touch_y;
|
||||
|
||||
static const FontSpec menu_font = { FONT_GOTHIC, FONT_WEIGHT_NORMAL, MODAL_FONT_SIZE };
|
||||
|
||||
static int text_width_cb(mu_Font font, const char *text, int len) {
|
||||
@@ -79,6 +95,8 @@ static void init_context(void) {
|
||||
ctx->text_width = text_width_cb;
|
||||
ctx->text_height = text_height_cb;
|
||||
ctx->style->font = (mu_Font)&menu_font;
|
||||
ctx->style->spacing = 8;
|
||||
ctx->style->thumb_size = 16;
|
||||
// microui uses style->size.y as the default height of a control row, so
|
||||
// set it to the font height to make each row tall enough for its text.
|
||||
ctx->style->size.y = text_height_cb(ctx->style->font);
|
||||
@@ -114,6 +132,32 @@ bool modal_default_handler(const SDL_Event *e, modal *modal) {
|
||||
mu_input_mouseup(ctx, e->button.x, e->button.y, b);
|
||||
break;
|
||||
}
|
||||
// Touch handling (see touch_phase above). The press/release is driven
|
||||
// from the frame loop in modal_run(), not emitted here directly.
|
||||
case SDL_FINGERDOWN:
|
||||
touch_x = e->tfinger.x * view_w;
|
||||
touch_y = e->tfinger.y * view_h;
|
||||
mu_input_mousemove(ctx, touch_x, touch_y);
|
||||
touch_phase = TOUCH_HOVER;
|
||||
touch_hover_frames = TOUCH_HOVER_FRAMES;
|
||||
touch_release_after_press = false;
|
||||
break;
|
||||
case SDL_FINGERMOTION:
|
||||
touch_x = e->tfinger.x * view_w;
|
||||
touch_y = e->tfinger.y * view_h;
|
||||
mu_input_mousemove(ctx, touch_x, touch_y);
|
||||
break;
|
||||
case SDL_FINGERUP:
|
||||
touch_x = e->tfinger.x * view_w;
|
||||
touch_y = e->tfinger.y * view_h;
|
||||
if (touch_phase == TOUCH_PRESS) {
|
||||
mu_input_mouseup(ctx, touch_x, touch_y, MU_MOUSE_LEFT);
|
||||
touch_phase = TOUCH_NONE;
|
||||
} else if (touch_phase == TOUCH_HOVER) {
|
||||
// Tap ended before the press was emitted; release right after it.
|
||||
touch_release_after_press = true;
|
||||
}
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP: {
|
||||
if (e->type == SDL_KEYDOWN && e->key.keysym.sym == SDLK_ESCAPE)
|
||||
@@ -238,6 +282,8 @@ static void modal_render(void) {
|
||||
}
|
||||
}
|
||||
SDL_RenderSetClipRect(gfx_renderer, NULL);
|
||||
SDL_SetRenderDrawColor(gfx_renderer, 0, 0, 0, 255);
|
||||
SDL_SetRenderDrawBlendMode(gfx_renderer, SDL_BLENDMODE_NONE);
|
||||
}
|
||||
|
||||
static bool modal_event_trampoline(const SDL_Event *e) {
|
||||
@@ -256,8 +302,25 @@ void modal_run(modal *m) {
|
||||
|
||||
mu_Id prev_hash = 0;
|
||||
|
||||
touch_phase = TOUCH_NONE;
|
||||
touch_release_after_press = false;
|
||||
bool open = true;
|
||||
while (open && !nact->is_quit) {
|
||||
// Drive the synthetic touch sequence.
|
||||
if (touch_phase == TOUCH_HOVER) {
|
||||
if (touch_hover_frames > 0) {
|
||||
touch_hover_frames--;
|
||||
} else {
|
||||
mu_input_mousedown(ctx, touch_x, touch_y, MU_MOUSE_LEFT);
|
||||
touch_phase = TOUCH_PRESS;
|
||||
}
|
||||
}
|
||||
if (touch_phase == TOUCH_PRESS && touch_release_after_press) {
|
||||
mu_input_mouseup(ctx, touch_x, touch_y, MU_MOUSE_LEFT);
|
||||
touch_phase = TOUCH_NONE;
|
||||
touch_release_after_press = false;
|
||||
}
|
||||
|
||||
event_get_key(); // pump SDL events -> trampoline -> m->handler -> microui
|
||||
|
||||
mu_begin(ctx);
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@
|
||||
#include "win/dialog.h"
|
||||
#endif
|
||||
|
||||
#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__)
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#define HAVE_VOLUME_VALANCER 1
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user