mirror of
https://github.com/Genymobile/scrcpy.git
synced 2026-04-21 01:33:36 +00:00
Merge a7118979c9 into cbc5917050
This commit is contained in:
commit
a3f594db20
10 changed files with 898 additions and 6 deletions
|
|
@ -98,6 +98,7 @@ enum {
|
|||
OPT_ORIENTATION,
|
||||
OPT_KEYBOARD,
|
||||
OPT_MOUSE,
|
||||
OPT_SCROLL_ACTION,
|
||||
OPT_HID_KEYBOARD_DEPRECATED,
|
||||
OPT_HID_MOUSE_DEPRECATED,
|
||||
OPT_NO_WINDOW,
|
||||
|
|
@ -114,6 +115,7 @@ enum {
|
|||
OPT_NO_VD_SYSTEM_DECORATIONS,
|
||||
OPT_NO_VD_DESTROY_CONTENT,
|
||||
OPT_DISPLAY_IME_POLICY,
|
||||
OPT_OVERLAY,
|
||||
};
|
||||
|
||||
struct sc_option {
|
||||
|
|
@ -205,6 +207,11 @@ static const struct sc_option options[] = {
|
|||
"This feature is only available with --audio-source=playback."
|
||||
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_OVERLAY,
|
||||
.longopt = "overlay",
|
||||
.text = "Keep the on-screen debug overlay visible (useful for debugging).",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_AUDIO_ENCODER,
|
||||
.longopt = "audio-encoder",
|
||||
|
|
@ -2230,6 +2237,22 @@ parse_gamepad(const char *optarg, enum sc_gamepad_input_mode *mode) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_scroll_action(const char *optarg, enum sc_scroll_action *action) {
|
||||
if (!strcmp(optarg, "scroll")) {
|
||||
*action = SC_SCROLL_ACTION_SCROLL;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strcmp(optarg, "zoom")) {
|
||||
*action = SC_SCROLL_ACTION_ZOOM;
|
||||
return true;
|
||||
}
|
||||
|
||||
LOGE("Unsupported scroll-action: %s (expected scroll or zoom)", optarg);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_time_limit(const char *s, sc_tick *tick) {
|
||||
long value;
|
||||
|
|
@ -2438,9 +2461,17 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||
return false;
|
||||
}
|
||||
break;
|
||||
case OPT_SCROLL_ACTION:
|
||||
if (!parse_scroll_action(optarg, &opts->scroll_action)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case OPT_NO_MOUSE_HOVER:
|
||||
opts->mouse_hover = false;
|
||||
break;
|
||||
case OPT_OVERLAY:
|
||||
opts->overlay_persistent = true;
|
||||
break;
|
||||
case OPT_HID_MOUSE_DEPRECATED:
|
||||
LOGE("--hid-mouse has been removed, use --mouse=aoa or "
|
||||
"--mouse=uhid instead.");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,163 @@
|
|||
#include "display.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
// Render the overlay UI: checkboxes, labels, and info line
|
||||
void sc_render_overlay_ui(struct sc_display *display) {
|
||||
// 8x12 font for ASCII 32..127 (printable)
|
||||
static const uint8_t font8x12[96][12] = {
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0}, // (space)
|
||||
{24,24,24,24,24,24,24,0,24,24,0,0}, // !
|
||||
{54,54,54,36,0,0,0,0,0,0,0,0}, // "
|
||||
{0,36,36,126,36,36,36,126,36,36,0,0}, // #
|
||||
{8,62,73,72,62,9,73,62,8,8,0,0}, // $
|
||||
{0,99,147,102,12,24,48,102,201,198,0,0}, // %
|
||||
{28,34,34,20,40,82,73,70,61,0,0,0}, // &
|
||||
{24,24,24,16,0,0,0,0,0,0,0,0}, // '
|
||||
{12,24,48,48,48,48,48,48,24,12,0,0}, // (
|
||||
{48,24,12,12,12,12,12,12,24,48,0,0}, // )
|
||||
{0,0,36,24,126,24,36,0,0,0,0,0}, // *
|
||||
{0,0,24,24,126,24,24,0,0,0,0,0}, // +
|
||||
{0,0,0,0,0,0,0,24,24,16,0,0}, // ,
|
||||
{0,0,0,0,126,0,0,0,0,0,0,0}, // -
|
||||
{0,0,0,0,0,0,0,24,24,0,0,0}, // .
|
||||
{0,3,6,12,24,48,96,192,128,0,0,0}, // /
|
||||
{60,66,99,115,91,79,70,66,60,0,0,0}, // 0
|
||||
{24,56,24,24,24,24,24,24,126,0,0,0}, // 1
|
||||
{60,66,3,6,12,24,48,96,127,0,0,0}, // 2
|
||||
{60,66,3,6,28,6,3,66,60,0,0,0}, // 3
|
||||
{6,14,30,54,102,127,6,6,15,0,0,0}, // 4
|
||||
{127,64,64,124,66,3,3,66,60,0,0,0}, // 5
|
||||
{28,48,96,124,102,99,99,102,60,0,0,0}, // 6
|
||||
{127,3,6,12,24,48,48,48,48,0,0,0}, // 7
|
||||
{60,66,99,102,60,102,99,66,60,0,0,0}, // 8
|
||||
{60,102,99,99,63,6,12,24,56,0,0,0}, // 9
|
||||
{0,0,24,24,0,0,24,24,0,0,0,0}, // :
|
||||
{0,0,24,24,0,0,24,24,16,0,0,0}, // ;
|
||||
{12,24,48,96,192,96,48,24,12,0,0,0}, // <
|
||||
{0,0,126,0,126,0,0,0,0,0,0,0}, // =
|
||||
{48,24,12,6,3,6,12,24,48,0,0,0}, // >
|
||||
{60,66,3,6,12,24,24,0,24,24,0,0}, // ?
|
||||
{60,66,99,111,107,111,96,62,0,0,0,0}, // @
|
||||
{24,60,102,102,126,102,102,102,231,0,0,0}, // A
|
||||
{124,102,102,124,102,102,102,102,124,0,0,0}, // B
|
||||
{60,102,96,96,96,96,96,102,60,0,0,0}, // C
|
||||
{120,108,102,102,102,102,102,108,120,0,0,0}, // D
|
||||
{126,96,96,124,96,96,96,96,126,0,0,0}, // E
|
||||
{126,96,96,124,96,96,96,96,96,0,0,0}, // F
|
||||
{60,102,96,96,110,102,102,102,60,0,0,0}, // G
|
||||
{102,102,102,126,102,102,102,102,102,0,0,0}, // H
|
||||
{60,24,24,24,24,24,24,24,60,0,0,0}, // I
|
||||
{6,6,6,6,6,6,6,102,60,0,0,0}, // J
|
||||
{102,108,120,112,120,108,102,102,102,0,0,0}, // K
|
||||
{96,96,96,96,96,96,96,96,126,0,0,0}, // L
|
||||
{99,119,127,107,99,99,99,99,99,0,0,0}, // M
|
||||
{102,102,118,126,110,102,102,102,102,0,0,0}, // N
|
||||
{60,102,99,99,99,99,99,102,60,0,0,0}, // O
|
||||
{124,102,102,102,124,96,96,96,96,0,0,0}, // P
|
||||
{60,102,99,99,99,99,107,102,61,0,0,0}, // Q
|
||||
{124,102,102,102,124,108,102,102,102,0,0,0}, // R
|
||||
{60,102,96,60,6,3,99,102,60,0,0,0}, // S
|
||||
{126,24,24,24,24,24,24,24,24,0,0,0}, // T
|
||||
{102,102,102,102,102,102,102,102,60,0,0,0}, // U
|
||||
{99,99,99,99,99,99,54,28,8,0,0,0}, // V
|
||||
{99,99,99,99,99,107,127,119,99,0,0,0}, // W
|
||||
{99,99,54,28,8,28,54,99,99,0,0,0}, // X
|
||||
{99,99,99,54,28,8,8,8,8,0,0,0}, // Y
|
||||
{127,3,6,12,24,48,96,96,127,0,0,0}, // Z
|
||||
{60,48,48,48,48,48,48,48,60,0,0,0}, // [
|
||||
{0,192,96,48,24,12,6,3,0,0,0,0}, // backslash
|
||||
{60,12,12,12,12,12,12,12,60,0,0,0}, // ]
|
||||
{8,28,54,99,0,0,0,0,0,0,0,0}, // ^
|
||||
{0,0,0,0,0,0,0,0,0,0,255,0}, // _
|
||||
{24,24,24,12,0,0,0,0,0,0,0,0}, // `
|
||||
{0,0,60,6,62,102,102,102,59,0,0,0}, // a
|
||||
{96,96,124,102,102,102,102,102,124,0,0,0}, // b
|
||||
{0,0,60,102,96,96,96,102,60,0,0,0}, // c
|
||||
{6,6,62,102,102,102,102,102,62,0,0,0}, // d
|
||||
{0,0,60,102,126,96,96,102,60,0,0,0}, // e
|
||||
{28,54,48,120,48,48,48,48,120,0,0,0}, // f
|
||||
{0,0,62,102,102,102,62,6,102,60,0,0}, // g
|
||||
{96,96,124,102,102,102,102,102,102,0,0,0}, // h
|
||||
{24,0,56,24,24,24,24,24,60,0,0,0}, // i
|
||||
{6,0,6,6,6,6,6,102,102,60,0,0}, // j
|
||||
{96,96,102,108,120,120,108,102,102,0,0,0}, // k
|
||||
{56,24,24,24,24,24,24,24,60,0,0,0}, // l
|
||||
{0,0,102,127,107,107,99,99,99,0,0,0}, // m
|
||||
{0,0,124,102,102,102,102,102,102,0,0,0}, // n
|
||||
{0,0,60,102,102,102,102,102,60,0,0,0}, // o
|
||||
{0,0,124,102,102,102,124,96,96,240,0,0}, // p
|
||||
{0,0,62,102,102,102,62,6,6,15,0,0}, // q
|
||||
{0,0,124,102,96,96,96,96,240,0,0,0}, // r
|
||||
{0,0,62,96,60,6,6,102,60,0,0,0}, // s
|
||||
{16,16,124,16,16,16,16,16,14,0,0,0}, // t
|
||||
{0,0,102,102,102,102,102,102,62,0,0,0}, // u
|
||||
{0,0,102,102,102,102,102,60,24,0,0,0}, // v
|
||||
{0,0,99,99,99,107,127,54,54,0,0,0}, // w
|
||||
{0,0,102,102,60,24,60,102,102,0,0,0}, // x
|
||||
{0,0,102,102,102,62,6,102,60,0,0,0}, // y
|
||||
{0,0,126,12,24,48,96,96,126,0,0,0}, // z
|
||||
{12,24,24,24,48,24,24,24,12,0,0,0}, // {
|
||||
{24,24,24,24,24,24,24,24,24,0,0,0}, // |
|
||||
{48,24,24,24,12,24,24,24,48,0,0,0}, // }
|
||||
{0,0,0,51,102,204,0,0,0,0,0,0}, // ~
|
||||
};
|
||||
const int char_w = 8;
|
||||
const int char_h = 12;
|
||||
const int spacing = 2;
|
||||
const int scale = 2;
|
||||
// Compose overlay lines
|
||||
char line1[64], line2[64], line3[128];
|
||||
snprintf(line1, sizeof(line1), "%s Pinch Zoom on Scroll", display->overlay_pinch_zoom_enabled ? "[x]" : "[ ]");
|
||||
snprintf(line2, sizeof(line2), "%s Overlay Toggle", display->overlay_toggle_enabled ? "[x]" : "[ ]");
|
||||
snprintf(line3, sizeof(line3), "%s", display->overlay_text);
|
||||
// Layout: 2 checkboxes + 1 info line
|
||||
int lines = 3;
|
||||
int maxlen = (int)strlen(line1);
|
||||
if ((int)strlen(line2) > maxlen) maxlen = (int)strlen(line2);
|
||||
if ((int)strlen(line3) > maxlen) maxlen = (int)strlen(line3);
|
||||
int text_w = maxlen * (char_w * scale + spacing);
|
||||
int text_h = lines * (char_h * scale) + (lines - 1) * (2 * scale);
|
||||
SDL_SetRenderDrawBlendMode(display->renderer, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(display->renderer, 0, 0, 0, 200); // dark semi-transparent background
|
||||
SDL_Rect bg = { display->overlay_x, display->overlay_y, text_w + 8, text_h + 8 };
|
||||
SDL_RenderFillRect(display->renderer, &bg);
|
||||
SDL_SetRenderDrawColor(display->renderer, 255, 255, 255, 255); // white text for readability
|
||||
int x = display->overlay_x + 4;
|
||||
int y = display->overlay_y + 4;
|
||||
const char *linestr[3] = { line1, line2, line3 };
|
||||
for (int l = 0; l < lines; ++l) {
|
||||
const char *text = linestr[l];
|
||||
int len = (int)strlen(text);
|
||||
int xx = x;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
unsigned char c = text[i];
|
||||
if (c < 32 || c > 127) {
|
||||
xx += char_w * scale + spacing;
|
||||
continue;
|
||||
}
|
||||
const uint8_t *glyph = font8x12[c - 32];
|
||||
for (int row = 0; row < char_h; ++row) {
|
||||
uint8_t bits = glyph[row];
|
||||
for (int col = 0; col < char_w; ++col) {
|
||||
if (bits & (1 << (7 - col))) {
|
||||
SDL_Rect r = { xx + col * scale, y + row * scale,
|
||||
scale, scale };
|
||||
SDL_RenderFillRect(display->renderer, &r);
|
||||
}
|
||||
}
|
||||
}
|
||||
xx += char_w * scale + spacing;
|
||||
}
|
||||
y += char_h * scale + 2 * scale;
|
||||
}
|
||||
// Set overlay_w and overlay_h for click detection
|
||||
display->overlay_w = text_w + 8;
|
||||
display->overlay_h = text_h + 8;
|
||||
}
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
|
@ -308,6 +466,9 @@ sc_display_render(struct sc_display *display, const SDL_Rect *geometry,
|
|||
if (!ok) {
|
||||
return SC_DISPLAY_RESULT_PENDING;
|
||||
}
|
||||
|
||||
SDL_RenderPresent(display->renderer);
|
||||
return SC_DISPLAY_RESULT_OK;
|
||||
}
|
||||
|
||||
SDL_Renderer *renderer = display->renderer;
|
||||
|
|
@ -346,6 +507,11 @@ sc_display_render(struct sc_display *display, const SDL_Rect *geometry,
|
|||
}
|
||||
}
|
||||
|
||||
// draw overlay UI (checkboxes, labels, info line) if requested
|
||||
if (display->overlay_enabled) {
|
||||
// Call the new overlay rendering function (to be implemented if not present)
|
||||
sc_render_overlay_ui(display);
|
||||
}
|
||||
SDL_RenderPresent(display->renderer);
|
||||
return SC_DISPLAY_RESULT_OK;
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +36,16 @@ struct sc_display {
|
|||
} pending;
|
||||
|
||||
bool has_frame;
|
||||
// transient overlay filled by the caller before rendering
|
||||
bool overlay_enabled;
|
||||
int overlay_x;
|
||||
int overlay_y;
|
||||
char overlay_text[128];
|
||||
int overlay_w;
|
||||
int overlay_h;
|
||||
// overlay checkbox state
|
||||
bool overlay_pinch_zoom_enabled;
|
||||
bool overlay_toggle_enabled;
|
||||
};
|
||||
|
||||
enum sc_display_result {
|
||||
|
|
@ -48,6 +58,9 @@ bool
|
|||
sc_display_init(struct sc_display *display, SDL_Window *window,
|
||||
SDL_Surface *icon_novideo, bool mipmaps);
|
||||
|
||||
void
|
||||
sc_render_overlay_ui(struct sc_display *display);
|
||||
|
||||
void
|
||||
sc_display_destroy(struct sc_display *display);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "android/input.h"
|
||||
|
|
@ -29,6 +30,8 @@ sc_input_manager_init(struct sc_input_manager *im,
|
|||
im->mp = params->mp;
|
||||
im->gp = params->gp;
|
||||
|
||||
im->scroll_action = params->scroll_action;
|
||||
|
||||
im->mouse_bindings = params->mouse_bindings;
|
||||
im->legacy_paste = params->legacy_paste;
|
||||
im->clipboard_autosync = params->clipboard_autosync;
|
||||
|
|
@ -267,7 +270,6 @@ clipboard_paste(struct sc_input_manager *im) {
|
|||
static void
|
||||
rotate_device(struct sc_input_manager *im) {
|
||||
assert(im->controller);
|
||||
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_ROTATE_DEVICE;
|
||||
|
||||
|
|
@ -336,6 +338,13 @@ simulate_virtual_finger(struct sc_input_manager *im,
|
|||
struct sc_point point) {
|
||||
bool up = action == AMOTION_EVENT_ACTION_UP;
|
||||
|
||||
fprintf(stderr,
|
||||
"INJECT: virtual finger action=%d pos=%d,%d pressure=%.1f\n",
|
||||
action,
|
||||
point.x,
|
||||
point.y,
|
||||
up ? 0.0f : 1.0f);
|
||||
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
||||
msg.inject_touch_event.action = action;
|
||||
|
|
@ -354,6 +363,160 @@ simulate_virtual_finger(struct sc_input_manager *im,
|
|||
return true;
|
||||
}
|
||||
|
||||
static struct sc_point
|
||||
inverse_point(struct sc_point point, struct sc_size size,
|
||||
bool invert_x, bool invert_y);
|
||||
|
||||
static SDL_Point
|
||||
sc_screen_convert_frame_to_window_point(struct sc_screen *screen,
|
||||
struct sc_point frame_point);
|
||||
|
||||
static void
|
||||
sc_input_manager_process_synthetic_mouse_motion(struct sc_input_manager *im,
|
||||
int32_t x, int32_t y,
|
||||
int32_t xrel, int32_t yrel);
|
||||
|
||||
static struct sc_position
|
||||
sc_input_manager_get_position(struct sc_input_manager *im, int32_t x,
|
||||
int32_t y);
|
||||
|
||||
static void
|
||||
sc_input_manager_process_synthetic_mouse_click(struct sc_input_manager *im,
|
||||
enum android_motionevent_action action,
|
||||
int32_t x, int32_t y);
|
||||
|
||||
static void
|
||||
sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
|
||||
const SDL_MouseMotionEvent *event);
|
||||
|
||||
bool
|
||||
sc_input_manager_handle_mouse_wheel_pinch(struct sc_input_manager *im,
|
||||
const SDL_MouseWheelEvent *event) {
|
||||
if (!im || !im->screen || event->y == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int mouse_x;
|
||||
int mouse_y;
|
||||
(void) SDL_GetMouseState(&mouse_x, &mouse_y);
|
||||
|
||||
// Ensure the wheel pinch originates from a point on the device video,
|
||||
// not from the overlay or outside the frame content.
|
||||
SDL_Rect rect = im->screen->rect;
|
||||
if (mouse_x < rect.x || mouse_x >= rect.x + rect.w
|
||||
|| mouse_y < rect.y || mouse_y >= rect.y + rect.h) {
|
||||
mouse_x = rect.x + rect.w / 2;
|
||||
mouse_y = rect.y + rect.h / 2;
|
||||
}
|
||||
|
||||
struct sc_point mouse = sc_screen_convert_window_to_frame_coords(
|
||||
im->screen, mouse_x, mouse_y);
|
||||
int zoom_delta = event->y > 0
|
||||
? (int)(im->screen->frame_size.height / 16)
|
||||
: -(int)(im->screen->frame_size.height / 16);
|
||||
|
||||
struct sc_point p1a = mouse;
|
||||
struct sc_point p2a = inverse_point(mouse, im->screen->frame_size,
|
||||
true, true);
|
||||
p2a.x = CLAMP(p2a.x, 0, im->screen->frame_size.width - 1);
|
||||
p2a.y = CLAMP(p2a.y, 0, im->screen->frame_size.height - 1);
|
||||
struct sc_point p1b;
|
||||
int32_t dx = mouse.x - im->screen->frame_size.width / 2;
|
||||
int32_t dy = mouse.y - im->screen->frame_size.height / 2;
|
||||
if (dx == 0 && dy == 0) {
|
||||
p1b.x = mouse.x;
|
||||
p1b.y = CLAMP(mouse.y + zoom_delta, 0,
|
||||
im->screen->frame_size.height - 1);
|
||||
} else {
|
||||
double dist = sqrt((double) dx * dx + (double) dy * dy);
|
||||
double scale = (double) zoom_delta / dist;
|
||||
p1b.x = CLAMP(mouse.x + (int32_t) round(dx * scale), 0,
|
||||
im->screen->frame_size.width - 1);
|
||||
p1b.y = CLAMP(mouse.y + (int32_t) round(dy * scale), 0,
|
||||
im->screen->frame_size.height - 1);
|
||||
}
|
||||
struct sc_point p2b = inverse_point(p1b, im->screen->frame_size,
|
||||
true, true);
|
||||
p2b.x = CLAMP(p2b.x, 0, im->screen->frame_size.width - 1);
|
||||
p2b.y = CLAMP(p2b.y, 0, im->screen->frame_size.height - 1);
|
||||
|
||||
fprintf(stderr,
|
||||
"PINCH: wheel gesture start mouse=%d,%d p1a=%d,%d p2a=%d,%d p1b=%d,%d p2b=%d,%d\n",
|
||||
mouse_x, mouse_y,
|
||||
p1a.x, p1a.y,
|
||||
p2a.x, p2a.y,
|
||||
p1b.x, p1b.y,
|
||||
p2b.x, p2b.y);
|
||||
fflush(stderr);
|
||||
|
||||
|
||||
SDL_Point target = sc_screen_convert_frame_to_window_point(im->screen,
|
||||
p1b);
|
||||
fprintf(stderr,
|
||||
"PINCH: synthetic motion target window=%d,%d frame=%d,%d vfinger=%d buttons=0x%02x\n",
|
||||
target.x, target.y,
|
||||
p1b.x, p1b.y,
|
||||
im->vfinger_down,
|
||||
im->mouse_buttons_state);
|
||||
fflush(stderr);
|
||||
|
||||
bool saved_vfinger_down = im->vfinger_down;
|
||||
bool saved_invert_x = im->vfinger_invert_x;
|
||||
bool saved_invert_y = im->vfinger_invert_y;
|
||||
uint8_t saved_buttons_state = im->mouse_buttons_state;
|
||||
|
||||
// Use the existing control-drag pinch flow for wheel zoom:
|
||||
// generic finger down, virtual finger down, synthetic move, then up.
|
||||
SDL_Point start = sc_screen_convert_frame_to_window_point(im->screen,
|
||||
p1a);
|
||||
SDL_Point end = sc_screen_convert_frame_to_window_point(im->screen,
|
||||
p1b);
|
||||
im->mouse_buttons_state = saved_buttons_state | SC_MOUSE_BUTTON_LEFT;
|
||||
sc_input_manager_process_synthetic_mouse_click(im,
|
||||
AMOTION_EVENT_ACTION_DOWN,
|
||||
start.x, start.y);
|
||||
if (!simulate_virtual_finger(im, AMOTION_EVENT_ACTION_DOWN, p2a)) {
|
||||
im->mouse_buttons_state = saved_buttons_state;
|
||||
return false;
|
||||
}
|
||||
|
||||
im->vfinger_invert_x = true;
|
||||
im->vfinger_invert_y = true;
|
||||
im->vfinger_down = true;
|
||||
im->mouse_buttons_state = saved_buttons_state | SC_MOUSE_BUTTON_LEFT;
|
||||
const int steps = 3;
|
||||
int32_t prev_x = mouse_x;
|
||||
int32_t prev_y = mouse_y;
|
||||
for (int i = 1; i <= steps; ++i) {
|
||||
int32_t step_x = mouse_x + (target.x - mouse_x) * i / steps;
|
||||
int32_t step_y = mouse_y + (target.y - mouse_y) * i / steps;
|
||||
int32_t step_xrel = step_x - prev_x;
|
||||
int32_t step_yrel = step_y - prev_y;
|
||||
sc_input_manager_process_synthetic_mouse_motion(im, step_x, step_y,
|
||||
step_xrel, step_yrel);
|
||||
prev_x = step_x;
|
||||
prev_y = step_y;
|
||||
}
|
||||
|
||||
sc_input_manager_process_synthetic_mouse_click(im,
|
||||
AMOTION_EVENT_ACTION_UP,
|
||||
end.x, end.y);
|
||||
if (!simulate_virtual_finger(im, AMOTION_EVENT_ACTION_UP, p2b)) {
|
||||
im->mouse_buttons_state = saved_buttons_state;
|
||||
im->vfinger_down = saved_vfinger_down;
|
||||
im->vfinger_invert_x = saved_invert_x;
|
||||
im->vfinger_invert_y = saved_invert_y;
|
||||
return false;
|
||||
}
|
||||
|
||||
im->mouse_buttons_state = saved_buttons_state;
|
||||
im->vfinger_down = saved_vfinger_down;
|
||||
im->vfinger_invert_x = saved_invert_x;
|
||||
im->vfinger_invert_y = saved_invert_y;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct sc_point
|
||||
inverse_point(struct sc_point point, struct sc_size size,
|
||||
bool invert_x, bool invert_y) {
|
||||
|
|
@ -366,6 +529,110 @@ inverse_point(struct sc_point point, struct sc_size size,
|
|||
return point;
|
||||
}
|
||||
|
||||
static SDL_Point
|
||||
sc_screen_convert_frame_to_window_point(struct sc_screen *screen,
|
||||
struct sc_point frame_point) {
|
||||
int32_t w = screen->content_size.width;
|
||||
int32_t h = screen->content_size.height;
|
||||
|
||||
struct sc_point drawable;
|
||||
switch (screen->orientation) {
|
||||
case SC_ORIENTATION_0:
|
||||
drawable.x = frame_point.x;
|
||||
drawable.y = frame_point.y;
|
||||
break;
|
||||
case SC_ORIENTATION_90:
|
||||
drawable.x = h - frame_point.y;
|
||||
drawable.y = frame_point.x;
|
||||
break;
|
||||
case SC_ORIENTATION_180:
|
||||
drawable.x = w - frame_point.x;
|
||||
drawable.y = h - frame_point.y;
|
||||
break;
|
||||
case SC_ORIENTATION_270:
|
||||
drawable.x = frame_point.y;
|
||||
drawable.y = w - frame_point.x;
|
||||
break;
|
||||
case SC_ORIENTATION_FLIP_0:
|
||||
drawable.x = w - frame_point.x;
|
||||
drawable.y = frame_point.y;
|
||||
break;
|
||||
case SC_ORIENTATION_FLIP_90:
|
||||
drawable.x = h - frame_point.y;
|
||||
drawable.y = w - frame_point.x;
|
||||
break;
|
||||
case SC_ORIENTATION_FLIP_180:
|
||||
drawable.x = frame_point.x;
|
||||
drawable.y = h - frame_point.y;
|
||||
break;
|
||||
default:
|
||||
assert(screen->orientation == SC_ORIENTATION_FLIP_270);
|
||||
drawable.x = frame_point.y;
|
||||
drawable.y = frame_point.x;
|
||||
break;
|
||||
}
|
||||
|
||||
int ww, wh, dw, dh;
|
||||
SDL_GetWindowSize(screen->window, &ww, &wh);
|
||||
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
|
||||
int32_t x = screen->rect.x + (int64_t) drawable.x * screen->rect.w / w;
|
||||
int32_t y = screen->rect.y + (int64_t) drawable.y * screen->rect.h / h;
|
||||
SDL_Point result = {
|
||||
.x = (int64_t) x * ww / dw,
|
||||
.y = (int64_t) y * wh / dh,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
sc_input_manager_process_synthetic_mouse_motion(struct sc_input_manager *im,
|
||||
int32_t x, int32_t y,
|
||||
int32_t xrel, int32_t yrel) {
|
||||
fprintf(stderr,
|
||||
"SYNTH: process motion x=%d y=%d xrel=%d yrel=%d vfinger=%d buttons=0x%02x\n",
|
||||
x, y, xrel, yrel, im->vfinger_down, im->mouse_buttons_state);
|
||||
fflush(stderr);
|
||||
SDL_MouseMotionEvent event = {
|
||||
.type = SDL_MOUSEMOTION,
|
||||
.which = 0,
|
||||
.x = x,
|
||||
.y = y,
|
||||
.xrel = xrel,
|
||||
.yrel = yrel,
|
||||
.state = SDL_BUTTON(SDL_BUTTON_LEFT),
|
||||
};
|
||||
sc_input_manager_process_mouse_motion(im, &event);
|
||||
}
|
||||
|
||||
static void
|
||||
sc_input_manager_process_synthetic_mouse_click(struct sc_input_manager *im,
|
||||
enum android_motionevent_action action,
|
||||
int32_t x, int32_t y) {
|
||||
if (!im || !im->mp || !im->mp->ops->process_mouse_click) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct sc_mouse_click_event evt = {
|
||||
.position = sc_input_manager_get_position(im, x, y),
|
||||
.action = action == AMOTION_EVENT_ACTION_DOWN
|
||||
? SC_ACTION_DOWN : SC_ACTION_UP,
|
||||
.button = SC_MOUSE_BUTTON_LEFT,
|
||||
.pointer_id = SC_POINTER_ID_GENERIC_FINGER,
|
||||
.buttons_state = action == AMOTION_EVENT_ACTION_DOWN
|
||||
? SC_MOUSE_BUTTON_LEFT : 0,
|
||||
};
|
||||
|
||||
fprintf(stderr,
|
||||
"SYNTH: process click action=%d x=%d y=%d buttons=0x%02x\n",
|
||||
action,
|
||||
x,
|
||||
y,
|
||||
evt.buttons_state);
|
||||
fflush(stderr);
|
||||
|
||||
im->mp->ops->process_mouse_click(im->mp, &evt);
|
||||
}
|
||||
|
||||
static void
|
||||
sc_input_manager_process_key(struct sc_input_manager *im,
|
||||
const SDL_KeyboardEvent *event) {
|
||||
|
|
@ -637,6 +904,12 @@ sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
|
|||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"SYNTH: mouse motion event x=%d y=%d xrel=%d yrel=%d buttons=0x%02x vfinger=%d\n",
|
||||
event->x, event->y, event->xrel, event->yrel,
|
||||
im->mouse_buttons_state, im->vfinger_down);
|
||||
fflush(stderr);
|
||||
|
||||
struct sc_mouse_motion_event evt = {
|
||||
.position = sc_input_manager_get_position(im, event->x, event->y),
|
||||
.pointer_id = im->vfinger_down ? SC_POINTER_ID_GENERIC_FINGER
|
||||
|
|
@ -657,6 +930,13 @@ sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
|
|||
struct sc_point mouse =
|
||||
sc_screen_convert_window_to_frame_coords(im->screen, event->x,
|
||||
event->y);
|
||||
LOGD("Virtual finger move: mouse=%d,%d frame=%d,%d invert_x=%d invert_y=%d",
|
||||
event->x,
|
||||
event->y,
|
||||
mouse.x,
|
||||
mouse.y,
|
||||
im->vfinger_invert_x,
|
||||
im->vfinger_invert_y);
|
||||
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size,
|
||||
im->vfinger_invert_x,
|
||||
im->vfinger_invert_y);
|
||||
|
|
@ -725,6 +1005,16 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
|||
bool paused = im->screen->paused;
|
||||
bool down = event->type == SDL_MOUSEBUTTONDOWN;
|
||||
|
||||
fprintf(stderr, "INPUT: mouse button %s button=%d clicks=%d pos=%d,%d which=%d control=%d paused=%d\n",
|
||||
down ? "down" : "up",
|
||||
event->button,
|
||||
event->clicks,
|
||||
event->x,
|
||||
event->y,
|
||||
event->which,
|
||||
control,
|
||||
paused);
|
||||
|
||||
enum sc_mouse_button button = sc_mouse_button_from_sdl(event->button);
|
||||
if (button == SC_MOUSE_BUTTON_UNKNOWN) {
|
||||
return;
|
||||
|
|
@ -747,6 +1037,10 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
|||
: &im->mouse_bindings.sec;
|
||||
enum sc_mouse_binding binding =
|
||||
sc_input_manager_get_binding(bindings, event->button);
|
||||
LOGD("Mouse button binding: button=%d -> binding=%d action=%s",
|
||||
event->button,
|
||||
binding,
|
||||
down ? "down" : "up");
|
||||
assert(binding != SC_MOUSE_BINDING_AUTO);
|
||||
switch (binding) {
|
||||
case SC_MOUSE_BINDING_DISABLED:
|
||||
|
|
@ -824,6 +1118,14 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
|||
.buttons_state = im->mouse_buttons_state,
|
||||
};
|
||||
|
||||
LOGD("Dispatching click event: pointer_id=%llu button=%d action=%d pos=%d,%d buttons_state=0x%02x",
|
||||
evt.pointer_id,
|
||||
evt.button,
|
||||
evt.action,
|
||||
(int) evt.position.point.x,
|
||||
(int) evt.position.point.y,
|
||||
evt.buttons_state);
|
||||
|
||||
assert(im->mp->ops->process_mouse_click);
|
||||
im->mp->ops->process_mouse_click(im->mp, &evt);
|
||||
|
||||
|
|
@ -866,6 +1168,13 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
|||
// 1 1 0 1 horizontal tilt
|
||||
im->vfinger_invert_x = ctrl_pressed ^ shift_pressed;
|
||||
im->vfinger_invert_y = ctrl_pressed;
|
||||
LOGD("Virtual finger mode start: invert_x=%d invert_y=%d mouse=%d,%d frame=%d,%d",
|
||||
im->vfinger_invert_x,
|
||||
im->vfinger_invert_y,
|
||||
event->x,
|
||||
event->y,
|
||||
mouse.x,
|
||||
mouse.y);
|
||||
}
|
||||
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size,
|
||||
im->vfinger_invert_x,
|
||||
|
|
@ -873,6 +1182,10 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
|||
enum android_motionevent_action action = down
|
||||
? AMOTION_EVENT_ACTION_DOWN
|
||||
: AMOTION_EVENT_ACTION_UP;
|
||||
LOGD("Virtual finger event: action=%d vfinger=%d,%d",
|
||||
(int) action,
|
||||
vfinger.x,
|
||||
vfinger.y);
|
||||
if (!simulate_virtual_finger(im, action, vfinger)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -883,7 +1196,56 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
|||
static void
|
||||
sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
|
||||
const SDL_MouseWheelEvent *event) {
|
||||
LOGD("Mouse wheel event: x=%d y=%d direction=%d preciseX=%.2f preciseY=%.2f scroll_action=%d",
|
||||
event->x,
|
||||
event->y,
|
||||
event->direction,
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
||||
event->preciseX,
|
||||
event->preciseY,
|
||||
#else
|
||||
0.0f,
|
||||
0.0f,
|
||||
#endif
|
||||
im->scroll_action);
|
||||
// If configured to map scroll to zoom, synthesize zoom key events
|
||||
if (im->scroll_action == SC_SCROLL_ACTION_ZOOM) {
|
||||
// Prefer integer delta when available
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
||||
int v = event->y; // integer ticks
|
||||
#else
|
||||
int v = event->y;
|
||||
#endif
|
||||
if (v == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
enum android_keycode key = v > 0 ? AKEYCODE_ZOOM_IN : AKEYCODE_ZOOM_OUT;
|
||||
|
||||
// For each tick, send a key down + key up
|
||||
int ticks = v > 0 ? v : -v;
|
||||
LOGD("Synthesizing zoom key events: key=%d ticks=%d", key, ticks);
|
||||
for (int i = 0; i < ticks; ++i) {
|
||||
struct sc_control_msg msg = {0};
|
||||
msg.type = SC_CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
||||
msg.inject_keycode.action = AKEY_EVENT_ACTION_DOWN;
|
||||
msg.inject_keycode.keycode = key;
|
||||
msg.inject_keycode.repeat = 0;
|
||||
msg.inject_keycode.metastate = 0;
|
||||
if (!sc_controller_push_msg(im->controller, &msg)) {
|
||||
LOGW("Could not request 'inject zoom key down'");
|
||||
}
|
||||
msg.inject_keycode.action = AKEY_EVENT_ACTION_UP;
|
||||
if (!sc_controller_push_msg(im->controller, &msg)) {
|
||||
LOGW("Could not request 'inject zoom key up'");
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!im->mp->ops->process_mouse_scroll) {
|
||||
LOGD("Mouse scroll drop: processor does not support scroll");
|
||||
// The mouse processor does not support scroll events
|
||||
return;
|
||||
}
|
||||
|
|
@ -908,6 +1270,59 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
|
|||
.buttons_state = im->mouse_buttons_state,
|
||||
};
|
||||
|
||||
LOGD("Mouse scroll forwarded: pos=%d,%d hscroll=%.2f vscroll=%.2f h_int=%d v_int=%d buttons_state=0x%02x",
|
||||
(int) evt.position.point.x,
|
||||
(int) evt.position.point.y,
|
||||
evt.hscroll,
|
||||
evt.vscroll,
|
||||
(int) evt.hscroll_int,
|
||||
(int) evt.vscroll_int,
|
||||
evt.buttons_state);
|
||||
|
||||
/* Debug logging: show scroll event details */
|
||||
/* Also print to stderr directly to ensure visibility even if SDL logs
|
||||
are redirected or filtered by the environment/IDE. */
|
||||
fprintf(stderr, "SCROLL: pos=%d,%d h=%.2f v=%.2f h_int=%d v_int=%d\n",
|
||||
(int) evt.position.point.x,
|
||||
(int) evt.position.point.y,
|
||||
evt.hscroll,
|
||||
evt.vscroll,
|
||||
(int) evt.hscroll_int,
|
||||
(int) evt.vscroll_int);
|
||||
fflush(stderr);
|
||||
|
||||
LOGI("mouse wheel event: pos=%d,%d h=%f v=%f h_int=%d v_int=%d",
|
||||
(int) evt.position.point.x,
|
||||
(int) evt.position.point.y,
|
||||
evt.hscroll,
|
||||
evt.vscroll,
|
||||
(int) evt.hscroll_int,
|
||||
(int) evt.vscroll_int);
|
||||
|
||||
/* Also update the window title briefly so it's visible on-screen */
|
||||
if (im->screen && im->screen->window) {
|
||||
char _titlebuf[128];
|
||||
snprintf(_titlebuf, sizeof _titlebuf, "scrcpy - scroll h=%.2f v=%.2f",
|
||||
evt.hscroll, evt.vscroll);
|
||||
SDL_SetWindowTitle(im->screen->window, _titlebuf);
|
||||
}
|
||||
|
||||
/* Update on-screen overlay so the user sees the scroll info visually */
|
||||
if (im->screen) {
|
||||
// Update the overlay info line for the new overlay UI
|
||||
snprintf(im->screen->overlay_text, sizeof im->screen->overlay_text,
|
||||
"h=%.2f v=%.2f x=%d y=%d",
|
||||
evt.hscroll, evt.vscroll,
|
||||
(int) evt.position.point.x, (int) evt.position.point.y);
|
||||
// Always show the overlay if persistent, otherwise show temporarily
|
||||
if (!im->screen->overlay_persistent) {
|
||||
im->screen->overlay_visible = true;
|
||||
im->screen->overlay_ttl = 120; // ~2 seconds at 60 FPS
|
||||
}
|
||||
LOGD("Overlay info updated from wheel: %s", im->screen->overlay_text);
|
||||
// If persistent, overlay_visible is managed elsewhere
|
||||
}
|
||||
|
||||
im->mp->ops->process_mouse_scroll(im->mp, &evt);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
#include <SDL2/SDL_events.h>
|
||||
#include <SDL2/SDL_keycode.h>
|
||||
|
||||
#include "android/input.h"
|
||||
|
||||
#include "controller.h"
|
||||
#include "file_pusher.h"
|
||||
#include "options.h"
|
||||
|
|
@ -44,6 +46,7 @@ struct sc_input_manager {
|
|||
uint16_t last_mod;
|
||||
|
||||
uint64_t next_sequence; // used for request acknowledgements
|
||||
enum sc_scroll_action scroll_action;
|
||||
};
|
||||
|
||||
struct sc_input_manager_params {
|
||||
|
|
@ -58,6 +61,7 @@ struct sc_input_manager_params {
|
|||
bool legacy_paste;
|
||||
bool clipboard_autosync;
|
||||
uint8_t shortcut_mods; // OR of enum sc_shortcut_mod values
|
||||
enum sc_scroll_action scroll_action;
|
||||
};
|
||||
|
||||
void
|
||||
|
|
@ -68,4 +72,8 @@ void
|
|||
sc_input_manager_handle_event(struct sc_input_manager *im,
|
||||
const SDL_Event *event);
|
||||
|
||||
bool
|
||||
sc_input_manager_handle_mouse_wheel_pinch(struct sc_input_manager *im,
|
||||
const SDL_MouseWheelEvent *event);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -108,11 +108,13 @@ const struct scrcpy_options scrcpy_options_default = {
|
|||
.window = true,
|
||||
.mouse_hover = true,
|
||||
.audio_dup = false,
|
||||
.overlay_persistent = false,
|
||||
.new_display = NULL,
|
||||
.start_app = NULL,
|
||||
.angle = NULL,
|
||||
.vd_destroy_content = true,
|
||||
.vd_system_decorations = true,
|
||||
.scroll_action = SC_SCROLL_ACTION_SCROLL,
|
||||
};
|
||||
|
||||
enum sc_orientation
|
||||
|
|
|
|||
|
|
@ -192,6 +192,11 @@ struct sc_mouse_binding_set {
|
|||
enum sc_mouse_binding click5;
|
||||
};
|
||||
|
||||
enum sc_scroll_action {
|
||||
SC_SCROLL_ACTION_SCROLL,
|
||||
SC_SCROLL_ACTION_ZOOM,
|
||||
};
|
||||
|
||||
struct sc_mouse_bindings {
|
||||
struct sc_mouse_binding_set pri;
|
||||
struct sc_mouse_binding_set sec; // When Shift is pressed
|
||||
|
|
@ -323,10 +328,14 @@ struct scrcpy_options {
|
|||
bool window;
|
||||
bool mouse_hover;
|
||||
bool audio_dup;
|
||||
/* Keep on-screen overlay visible (debug) */
|
||||
bool overlay_persistent;
|
||||
const char *new_display; // [<width>x<height>][/<dpi>] parsed by the server
|
||||
const char *start_app;
|
||||
bool vd_destroy_content;
|
||||
bool vd_system_decorations;
|
||||
/* Behavior for mouse wheel: scroll (default) or zoom (send zoom keycodes) */
|
||||
enum sc_scroll_action scroll_action;
|
||||
};
|
||||
|
||||
extern const struct scrcpy_options scrcpy_options_default;
|
||||
|
|
|
|||
|
|
@ -813,6 +813,7 @@ aoa_complete:
|
|||
.legacy_paste = options->legacy_paste,
|
||||
.clipboard_autosync = options->clipboard_autosync,
|
||||
.shortcut_mods = options->shortcut_mods,
|
||||
.scroll_action = options->scroll_action,
|
||||
.window_title = window_title,
|
||||
.always_on_top = options->always_on_top,
|
||||
.window_x = options->window_x,
|
||||
|
|
@ -824,6 +825,7 @@ aoa_complete:
|
|||
.mipmaps = options->mipmaps,
|
||||
.fullscreen = options->fullscreen,
|
||||
.start_fps_counter = options->start_fps_counter,
|
||||
.overlay_persistent = options->overlay_persistent,
|
||||
};
|
||||
|
||||
if (!sc_screen_init(&s->screen, &screen_params)) {
|
||||
|
|
|
|||
236
app/src/screen.c
236
app/src/screen.c
|
|
@ -162,6 +162,9 @@ sc_screen_is_relative_mode(struct sc_screen *screen) {
|
|||
return screen->im.mp && screen->im.mp->relative_mode;
|
||||
}
|
||||
|
||||
static void sc_screen_log_overlay_state(struct sc_screen *screen,
|
||||
const char *reason);
|
||||
|
||||
static void
|
||||
sc_screen_update_content_rect(struct sc_screen *screen) {
|
||||
assert(screen->video);
|
||||
|
|
@ -213,9 +216,45 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
|
|||
sc_screen_update_content_rect(screen);
|
||||
}
|
||||
|
||||
// Show overlay when the toggle is enabled or when a temporary overlay TTL is active
|
||||
bool overlay_should_be_visible = screen->overlay_toggle_enabled || screen->overlay_ttl > 0;
|
||||
if (overlay_should_be_visible != screen->overlay_visible) {
|
||||
screen->overlay_visible = overlay_should_be_visible;
|
||||
sc_screen_log_overlay_state(screen, "render visibility update");
|
||||
}
|
||||
if (screen->overlay_visible) {
|
||||
screen->display.overlay_enabled = true;
|
||||
screen->display.overlay_x = screen->overlay_x;
|
||||
screen->display.overlay_y = screen->overlay_y;
|
||||
// copy info line
|
||||
strncpy(screen->display.overlay_text, screen->overlay_text,
|
||||
sizeof(screen->display.overlay_text) - 1);
|
||||
screen->display.overlay_text[sizeof(screen->display.overlay_text) - 1] = '\0';
|
||||
// forward checkbox state
|
||||
screen->display.overlay_pinch_zoom_enabled = screen->overlay_pinch_zoom_enabled;
|
||||
screen->display.overlay_toggle_enabled = screen->overlay_toggle_enabled;
|
||||
// forward overlay size for click detection
|
||||
screen->overlay_w = screen->display.overlay_w;
|
||||
screen->overlay_h = screen->display.overlay_h;
|
||||
} else {
|
||||
screen->display.overlay_enabled = false;
|
||||
screen->display.overlay_text[0] = '\0';
|
||||
screen->display.overlay_pinch_zoom_enabled = false;
|
||||
screen->display.overlay_toggle_enabled = false;
|
||||
}
|
||||
|
||||
enum sc_display_result res =
|
||||
sc_display_render(&screen->display, &screen->rect, screen->orientation);
|
||||
(void) res; // any error already logged
|
||||
|
||||
// decrement overlay TTL (frames)
|
||||
if (screen->overlay_ttl > 0) {
|
||||
--screen->overlay_ttl;
|
||||
if (screen->overlay_ttl == 0) {
|
||||
LOGD("Overlay TTL expired, hiding overlay");
|
||||
screen->overlay_visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -249,7 +288,6 @@ event_watcher(void *data, SDL_Event *event) {
|
|||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool
|
||||
sc_screen_frame_sink_open(struct sc_frame_sink *sink,
|
||||
const AVCodecContext *ctx) {
|
||||
|
|
@ -343,6 +381,30 @@ sc_screen_init(struct sc_screen *screen,
|
|||
screen->req.fullscreen = params->fullscreen;
|
||||
screen->req.start_fps_counter = params->start_fps_counter;
|
||||
|
||||
// overlay defaults
|
||||
screen->overlay_visible = false;
|
||||
screen->overlay_x = 20;
|
||||
screen->overlay_y = 20;
|
||||
screen->overlay_w = 220;
|
||||
screen->overlay_h = 36;
|
||||
screen->overlay_text[0] = '\0';
|
||||
screen->overlay_ttl = 0;
|
||||
screen->overlay_dragging = false;
|
||||
screen->overlay_drag_offset_x = 0;
|
||||
screen->overlay_drag_offset_y = 0;
|
||||
/* persistent overlay requested by options */
|
||||
screen->overlay_persistent = params->overlay_persistent;
|
||||
if (screen->overlay_persistent) {
|
||||
screen->overlay_visible = true;
|
||||
LOGD("Overlay persistent enabled at init");
|
||||
}
|
||||
// Initialize new overlay checkboxes
|
||||
screen->overlay_pinch_zoom_enabled = false;
|
||||
screen->overlay_toggle_enabled = screen->overlay_persistent;
|
||||
if (screen->overlay_toggle_enabled) {
|
||||
screen->overlay_visible = true;
|
||||
}
|
||||
|
||||
bool ok = sc_frame_buffer_init(&screen->fb);
|
||||
if (!ok) {
|
||||
return false;
|
||||
|
|
@ -440,6 +502,7 @@ sc_screen_init(struct sc_screen *screen,
|
|||
.legacy_paste = params->legacy_paste,
|
||||
.clipboard_autosync = params->clipboard_autosync,
|
||||
.shortcut_mods = params->shortcut_mods,
|
||||
.scroll_action = params->scroll_action,
|
||||
};
|
||||
|
||||
sc_input_manager_init(&screen->im, &im_params);
|
||||
|
|
@ -598,9 +661,7 @@ sc_screen_set_orientation(struct sc_screen *screen,
|
|||
set_content_size(screen, new_content_size);
|
||||
|
||||
screen->orientation = orientation;
|
||||
LOGI("Display orientation set to %s", sc_orientation_get_name(orientation));
|
||||
|
||||
sc_screen_render(screen, true);
|
||||
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -798,8 +859,77 @@ sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) {
|
|||
content_size.height);
|
||||
}
|
||||
|
||||
static bool
|
||||
sc_screen_handle_mouse_wheel_pinch(struct sc_screen *screen,
|
||||
const SDL_MouseWheelEvent *wheel) {
|
||||
if (!screen->overlay_pinch_zoom_enabled) {
|
||||
fprintf(stderr, "PINCH: wheel ignored, pinch disabled\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(stderr, "PINCH: intercepting mouse wheel for pinch zoom\n");
|
||||
LOGI("Intercepting mouse wheel for pinch zoom: visible=%d toggle_enabled=%d pinch_enabled=%d",
|
||||
screen->overlay_visible,
|
||||
screen->overlay_toggle_enabled,
|
||||
screen->overlay_pinch_zoom_enabled);
|
||||
return sc_input_manager_handle_mouse_wheel_pinch(&screen->im, wheel);
|
||||
}
|
||||
|
||||
static void
|
||||
sc_screen_log_overlay_state(struct sc_screen *screen, const char *reason) {
|
||||
LOGD("Overlay state changed (%s): visible=%d persistent=%d toggle_enabled=%d pinch_zoom_enabled=%d ttl=%d",
|
||||
reason,
|
||||
screen->overlay_visible,
|
||||
screen->overlay_persistent,
|
||||
screen->overlay_toggle_enabled,
|
||||
screen->overlay_pinch_zoom_enabled,
|
||||
screen->overlay_ttl);
|
||||
}
|
||||
|
||||
bool
|
||||
sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
|
||||
|
||||
// Overlay toggle hotkey: F10
|
||||
if (event->type == SDL_KEYDOWN && event->key.keysym.sym == SDLK_F10) {
|
||||
screen->overlay_toggle_enabled = !screen->overlay_toggle_enabled;
|
||||
if (!screen->overlay_toggle_enabled) {
|
||||
screen->overlay_visible = false;
|
||||
} else {
|
||||
screen->overlay_visible = true;
|
||||
}
|
||||
sc_screen_log_overlay_state(screen, "hotkey F10");
|
||||
return true;
|
||||
}
|
||||
|
||||
// If overlay toggle is off, keep normal event flow and allow the overlay
|
||||
// to remain visible temporarily while the TTL is active.
|
||||
if (!screen->overlay_toggle_enabled) {
|
||||
// Allow toggling overlay via checkbox click
|
||||
if (event->type == SDL_MOUSEBUTTONDOWN) {
|
||||
int mx = event->button.x;
|
||||
int my = event->button.y;
|
||||
sc_screen_hidpi_scale_coords(screen, &mx, &my);
|
||||
int char_h = 12, scale = 2;
|
||||
int x = screen->overlay_x + 4;
|
||||
int y2 = screen->overlay_y + 4 + char_h * scale + 2 * scale;
|
||||
int cb_w = screen->overlay_w - 8;
|
||||
int cb_h = char_h * scale;
|
||||
if (mx >= x && mx < x + cb_w && my >= y2 && my < y2 + cb_h) {
|
||||
screen->overlay_toggle_enabled = true;
|
||||
screen->overlay_visible = true;
|
||||
LOGD("Overlay toggle re-enabled via hidden click");
|
||||
fprintf(stderr, "OVERLAY: re-enabled via hidden click\n");
|
||||
fprintf(stderr, "OVERLAY STATE: toggle=%d pinch=%d visible=%d ttl=%d\n",
|
||||
screen->overlay_toggle_enabled,
|
||||
screen->overlay_pinch_zoom_enabled,
|
||||
screen->overlay_visible,
|
||||
screen->overlay_ttl);
|
||||
sc_screen_log_overlay_state(screen, "hidden click on toggle line");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (event->type) {
|
||||
case SC_EVENT_SCREEN_INIT_SIZE: {
|
||||
// The initial size is passed via screen->frame_size
|
||||
|
|
@ -818,6 +948,12 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
case SDL_MOUSEWHEEL: {
|
||||
if (sc_screen_handle_mouse_wheel_pinch(screen, &event->wheel)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_WINDOWEVENT:
|
||||
if (!screen->video
|
||||
&& event->window.event == SDL_WINDOWEVENT_EXPOSED) {
|
||||
|
|
@ -867,6 +1003,98 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Handle overlay drag/move before forwarding to input manager
|
||||
switch (event->type) {
|
||||
case SDL_MOUSEBUTTONDOWN: {
|
||||
fprintf(stderr, "SCREEN MOUSE DOWN: x=%d y=%d button=%d overlay_visible=%d toggle_enabled=%d pinch_enabled=%d\n",
|
||||
event->button.x,
|
||||
event->button.y,
|
||||
event->button.button,
|
||||
screen->overlay_visible,
|
||||
screen->overlay_toggle_enabled,
|
||||
screen->overlay_pinch_zoom_enabled);
|
||||
// Overlay checkbox click handling
|
||||
if (screen->overlay_visible && event->button.button == SDL_BUTTON_LEFT) {
|
||||
int mx = event->button.x;
|
||||
int my = event->button.y;
|
||||
sc_screen_hidpi_scale_coords(screen, &mx, &my);
|
||||
// Overlay layout must match display.c rendering
|
||||
// Font: 8x12, scale: 2, spacing: 2, padding: 4, 3 lines
|
||||
int char_h = 12, scale = 2;
|
||||
int x = screen->overlay_x + 4;
|
||||
int y = screen->overlay_y + 4;
|
||||
// Checkbox area: full visible line width for easier clicking
|
||||
int cb_w = screen->overlay_w - 8;
|
||||
int cb_h = char_h * scale;
|
||||
// Pinch Zoom checkbox (line 1)
|
||||
if (mx >= x && mx < x + cb_w && my >= y && my < y + cb_h) {
|
||||
screen->overlay_pinch_zoom_enabled = !screen->overlay_pinch_zoom_enabled;
|
||||
LOGD("Overlay pinch zoom toggled: %d", screen->overlay_pinch_zoom_enabled);
|
||||
fprintf(stderr, "OVERLAY: pinch zoom toggled=%d\n",
|
||||
screen->overlay_pinch_zoom_enabled);
|
||||
sc_screen_log_overlay_state(screen, "checkbox pinch zoom");
|
||||
return true;
|
||||
}
|
||||
// Overlay Toggle checkbox (line 2)
|
||||
int y2 = y + char_h * scale + 2 * scale;
|
||||
if (mx >= x && mx < x + cb_w && my >= y2 && my < y2 + cb_h) {
|
||||
screen->overlay_toggle_enabled = !screen->overlay_toggle_enabled;
|
||||
screen->overlay_visible = screen->overlay_toggle_enabled;
|
||||
LOGD("Overlay toggle toggled: %d", screen->overlay_toggle_enabled);
|
||||
fprintf(stderr, "OVERLAY: toggle enabled=%d\n",
|
||||
screen->overlay_toggle_enabled);
|
||||
sc_screen_log_overlay_state(screen, "checkbox overlay toggle");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (screen->overlay_visible && event->button.button == SDL_BUTTON_LEFT) {
|
||||
int mx = event->button.x;
|
||||
int my = event->button.y;
|
||||
sc_screen_hidpi_scale_coords(screen, &mx, &my);
|
||||
int char_h = 12, scale = 2;
|
||||
int drag_h = char_h * scale + 2 * scale;
|
||||
int drag_w = 3 * (8 * scale + 2) + 8;
|
||||
// Only start dragging if the click is in the top overlay header
|
||||
if (mx >= screen->overlay_x && mx < screen->overlay_x + drag_w
|
||||
&& my >= screen->overlay_y && my < screen->overlay_y + drag_h) {
|
||||
screen->overlay_dragging = true;
|
||||
screen->overlay_drag_offset_x = mx - screen->overlay_x;
|
||||
screen->overlay_drag_offset_y = my - screen->overlay_y;
|
||||
// consume the event (do not forward to device)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEBUTTONUP: {
|
||||
if (screen->overlay_dragging && event->button.button == SDL_BUTTON_LEFT) {
|
||||
screen->overlay_dragging = false;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEMOTION: {
|
||||
if (screen->overlay_dragging) {
|
||||
int mx = event->motion.x;
|
||||
int my = event->motion.y;
|
||||
sc_screen_hidpi_scale_coords(screen, &mx, &my);
|
||||
screen->overlay_x = mx - screen->overlay_drag_offset_x;
|
||||
screen->overlay_y = my - screen->overlay_drag_offset_y;
|
||||
// clamp to window bounds
|
||||
int ww, wh;
|
||||
SDL_GL_GetDrawableSize(screen->window, &ww, &wh);
|
||||
if (screen->overlay_x < 0) screen->overlay_x = 0;
|
||||
if (screen->overlay_y < 0) screen->overlay_y = 0;
|
||||
if (screen->overlay_x + screen->overlay_w > ww)
|
||||
screen->overlay_x = ww - screen->overlay_w;
|
||||
if (screen->overlay_y + screen->overlay_h > wh)
|
||||
screen->overlay_y = wh - screen->overlay_h;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sc_input_manager_handle_event(&screen->im, event);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,22 @@ struct sc_screen {
|
|||
|
||||
bool paused;
|
||||
AVFrame *resume_frame;
|
||||
|
||||
// simple debug overlay (movable)
|
||||
bool overlay_visible;
|
||||
bool overlay_persistent;
|
||||
int overlay_x;
|
||||
int overlay_y;
|
||||
int overlay_w;
|
||||
int overlay_h;
|
||||
char overlay_text[128];
|
||||
int overlay_ttl; // frames remaining to show
|
||||
bool overlay_dragging;
|
||||
int overlay_drag_offset_x;
|
||||
int overlay_drag_offset_y;
|
||||
// New: overlay checkboxes
|
||||
bool overlay_pinch_zoom_enabled;
|
||||
bool overlay_toggle_enabled;
|
||||
};
|
||||
|
||||
struct sc_screen_params {
|
||||
|
|
@ -100,6 +116,8 @@ struct sc_screen_params {
|
|||
|
||||
bool fullscreen;
|
||||
bool start_fps_counter;
|
||||
enum sc_scroll_action scroll_action;
|
||||
bool overlay_persistent;
|
||||
};
|
||||
|
||||
// initialize screen, create window, renderer and texture (window is hidden)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue