Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fba7f77a7e |
@@ -133,7 +133,7 @@ static int
|
|||||||
event_watcher(void *data, SDL_Event *event) {
|
event_watcher(void *data, SDL_Event *event) {
|
||||||
(void) data;
|
(void) data;
|
||||||
if (event->type == SDL_WINDOWEVENT
|
if (event->type == SDL_WINDOWEVENT
|
||||||
&& event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
|
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||||
// In practice, it seems to always be called from the same thread in
|
// In practice, it seems to always be called from the same thread in
|
||||||
// that specific case. Anyway, it's just a workaround.
|
// that specific case. Anyway, it's just a workaround.
|
||||||
screen_render(&screen);
|
screen_render(&screen);
|
||||||
|
|||||||
149
app/src/screen.c
149
app/src/screen.c
@@ -30,10 +30,10 @@ get_rotated_size(struct size size, int rotation) {
|
|||||||
|
|
||||||
// get the window size in a struct size
|
// get the window size in a struct size
|
||||||
static struct size
|
static struct size
|
||||||
get_window_size(const struct screen *screen) {
|
get_window_size(SDL_Window *window) {
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
SDL_GetWindowSize(screen->window, &width, &height);
|
SDL_GetWindowSize(window, &width, &height);
|
||||||
|
|
||||||
struct size size;
|
struct size size;
|
||||||
size.width = width;
|
size.width = width;
|
||||||
@@ -41,12 +41,31 @@ get_window_size(const struct screen *screen) {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the windowed window size
|
||||||
|
static struct size
|
||||||
|
get_windowed_window_size(const struct screen *screen) {
|
||||||
|
if (screen->fullscreen || screen->maximized) {
|
||||||
|
return screen->windowed_window_size;
|
||||||
|
}
|
||||||
|
return get_window_size(screen->window);
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply the windowed window size if fullscreen and maximized are disabled
|
||||||
|
static void
|
||||||
|
apply_windowed_size(struct screen *screen) {
|
||||||
|
if (!screen->fullscreen && !screen->maximized) {
|
||||||
|
SDL_SetWindowSize(screen->window, screen->windowed_window_size.width,
|
||||||
|
screen->windowed_window_size.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set the window size to be applied when fullscreen is disabled
|
// set the window size to be applied when fullscreen is disabled
|
||||||
static void
|
static void
|
||||||
set_window_size(struct screen *screen, struct size new_size) {
|
set_window_size(struct screen *screen, struct size new_size) {
|
||||||
assert(!screen->fullscreen);
|
// setting the window size during fullscreen is implementation defined,
|
||||||
assert(!screen->maximized);
|
// so apply the resize only after fullscreen is disabled
|
||||||
SDL_SetWindowSize(screen->window, new_size.width, new_size.height);
|
screen->windowed_window_size = new_size;
|
||||||
|
apply_windowed_size(screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the preferred display bounds (i.e. the screen bounds with some margins)
|
// get the preferred display bounds (i.e. the screen bounds with some margins)
|
||||||
@@ -119,8 +138,8 @@ get_optimal_size(struct size current_size, struct size content_size) {
|
|||||||
// same as get_optimal_size(), but read the current size from the window
|
// same as get_optimal_size(), but read the current size from the window
|
||||||
static inline struct size
|
static inline struct size
|
||||||
get_optimal_window_size(const struct screen *screen, struct size content_size) {
|
get_optimal_window_size(const struct screen *screen, struct size content_size) {
|
||||||
struct size window_size = get_window_size(screen);
|
struct size windowed_size = get_windowed_window_size(screen);
|
||||||
return get_optimal_size(window_size, content_size);
|
return get_optimal_size(windowed_size, content_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// initially, there is no current size, so use the frame size as current size
|
// initially, there is no current size, so use the frame size as current size
|
||||||
@@ -289,6 +308,8 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
screen->windowed_window_size = window_size;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,45 +331,6 @@ screen_destroy(struct screen *screen) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
resize_for_content(struct screen *screen, struct size old_content_size,
|
|
||||||
struct size new_content_size) {
|
|
||||||
struct size window_size = get_window_size(screen);
|
|
||||||
struct size target_size = {
|
|
||||||
.width = (uint32_t) window_size.width * new_content_size.width
|
|
||||||
/ old_content_size.width,
|
|
||||||
.height = (uint32_t) window_size.height * new_content_size.height
|
|
||||||
/ old_content_size.height,
|
|
||||||
};
|
|
||||||
target_size = get_optimal_size(target_size, new_content_size);
|
|
||||||
set_window_size(screen, target_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
set_content_size(struct screen *screen, struct size new_content_size) {
|
|
||||||
if (!screen->fullscreen && !screen->maximized) {
|
|
||||||
resize_for_content(screen, screen->content_size, new_content_size);
|
|
||||||
} else if (!screen->resize_pending) {
|
|
||||||
// Store the windowed size to be able to compute the optimal size once
|
|
||||||
// fullscreen and maximized are disabled
|
|
||||||
screen->windowed_content_size = screen->content_size;
|
|
||||||
screen->resize_pending = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
screen->content_size = new_content_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
apply_pending_resize(struct screen *screen) {
|
|
||||||
assert(!screen->fullscreen);
|
|
||||||
assert(!screen->maximized);
|
|
||||||
if (screen->resize_pending) {
|
|
||||||
resize_for_content(screen, screen->windowed_content_size,
|
|
||||||
screen->content_size);
|
|
||||||
screen->resize_pending = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_set_rotation(struct screen *screen, unsigned rotation) {
|
screen_set_rotation(struct screen *screen, unsigned rotation) {
|
||||||
assert(rotation < 4);
|
assert(rotation < 4);
|
||||||
@@ -356,6 +338,7 @@ screen_set_rotation(struct screen *screen, unsigned rotation) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct size old_content_size = screen->content_size;
|
||||||
struct size new_content_size =
|
struct size new_content_size =
|
||||||
get_rotated_size(screen->frame_size, rotation);
|
get_rotated_size(screen->frame_size, rotation);
|
||||||
|
|
||||||
@@ -366,7 +349,17 @@ screen_set_rotation(struct screen *screen, unsigned rotation) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_content_size(screen, new_content_size);
|
struct size windowed_size = get_windowed_window_size(screen);
|
||||||
|
struct size target_size = {
|
||||||
|
.width = (uint32_t) windowed_size.width * new_content_size.width
|
||||||
|
/ old_content_size.width,
|
||||||
|
.height = (uint32_t) windowed_size.height * new_content_size.height
|
||||||
|
/ old_content_size.height,
|
||||||
|
};
|
||||||
|
target_size = get_optimal_size(target_size, new_content_size);
|
||||||
|
set_window_size(screen, target_size);
|
||||||
|
|
||||||
|
screen->content_size = new_content_size;
|
||||||
screen->rotation = rotation;
|
screen->rotation = rotation;
|
||||||
LOGI("Display rotation set to %u", rotation);
|
LOGI("Display rotation set to %u", rotation);
|
||||||
|
|
||||||
@@ -390,8 +383,19 @@ prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
|||||||
// frame dimension changed, destroy texture
|
// frame dimension changed, destroy texture
|
||||||
SDL_DestroyTexture(screen->texture);
|
SDL_DestroyTexture(screen->texture);
|
||||||
|
|
||||||
set_content_size(screen, new_content_size);
|
struct size content_size = screen->content_size;
|
||||||
|
struct size windowed_size = get_windowed_window_size(screen);
|
||||||
|
struct size target_size = {
|
||||||
|
(uint32_t) windowed_size.width * new_content_size.width
|
||||||
|
/ content_size.width,
|
||||||
|
(uint32_t) windowed_size.height * new_content_size.height
|
||||||
|
/ content_size.height,
|
||||||
|
};
|
||||||
|
target_size = get_optimal_size(target_size, new_content_size);
|
||||||
|
set_window_size(screen, target_size);
|
||||||
|
|
||||||
screen->frame_size = new_frame_size;
|
screen->frame_size = new_frame_size;
|
||||||
|
screen->content_size = new_content_size;
|
||||||
|
|
||||||
LOGI("New texture: %" PRIu16 "x%" PRIu16,
|
LOGI("New texture: %" PRIu16 "x%" PRIu16,
|
||||||
screen->frame_size.width, screen->frame_size.height);
|
screen->frame_size.width, screen->frame_size.height);
|
||||||
@@ -474,9 +478,7 @@ screen_switch_fullscreen(struct screen *screen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
screen->fullscreen = !screen->fullscreen;
|
screen->fullscreen = !screen->fullscreen;
|
||||||
if (!screen->fullscreen && !screen->maximized) {
|
apply_windowed_size(screen);
|
||||||
apply_pending_resize(screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGD("Switched to %s mode", screen->fullscreen ? "fullscreen" : "windowed");
|
LOGD("Switched to %s mode", screen->fullscreen ? "fullscreen" : "windowed");
|
||||||
screen_render(screen);
|
screen_render(screen);
|
||||||
@@ -517,26 +519,6 @@ screen_resize_to_pixel_perfect(struct screen *screen) {
|
|||||||
content_size.height);
|
content_size.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool
|
|
||||||
is_fullscreen(const struct screen *screen) {
|
|
||||||
uint32_t flags = SDL_GetWindowFlags(screen->window);
|
|
||||||
return !!(flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
update_fullscreen_state(struct screen *screen) {
|
|
||||||
// There is no SDL event to detect fullscreen changes, so store the
|
|
||||||
// state in a field and compare on every "size changed" event
|
|
||||||
bool fullscreen = is_fullscreen(screen);
|
|
||||||
if (fullscreen != screen->fullscreen) {
|
|
||||||
// Fullscreen state have changed
|
|
||||||
screen->fullscreen = fullscreen;
|
|
||||||
if (!fullscreen && !screen->maximized) {
|
|
||||||
apply_pending_resize(screen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_handle_window_event(struct screen *screen,
|
screen_handle_window_event(struct screen *screen,
|
||||||
const SDL_WindowEvent *event) {
|
const SDL_WindowEvent *event) {
|
||||||
@@ -545,15 +527,36 @@ screen_handle_window_event(struct screen *screen,
|
|||||||
screen_render(screen);
|
screen_render(screen);
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||||
update_fullscreen_state(screen);
|
if (!screen->fullscreen && !screen->maximized) {
|
||||||
|
// Backup the previous size: if we receive the MAXIMIZED event,
|
||||||
|
// then the new size must be ignored (it's the maximized size).
|
||||||
|
// We could not rely on the window flags due to race conditions
|
||||||
|
// (they could be updated asynchronously, at least on X11).
|
||||||
|
screen->windowed_window_size_backup =
|
||||||
|
screen->windowed_window_size;
|
||||||
|
|
||||||
|
// Save the windowed size, so that it is available once the
|
||||||
|
// window is maximized or fullscreen is enabled.
|
||||||
|
screen->windowed_window_size = get_window_size(screen->window);
|
||||||
|
}
|
||||||
screen_render(screen);
|
screen_render(screen);
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_MAXIMIZED:
|
case SDL_WINDOWEVENT_MAXIMIZED:
|
||||||
|
// The backup size must be non-nul.
|
||||||
|
assert(screen->windowed_window_size_backup.width);
|
||||||
|
assert(screen->windowed_window_size_backup.height);
|
||||||
|
// Revert the last size, it was updated while screen was maximized.
|
||||||
|
screen->windowed_window_size = screen->windowed_window_size_backup;
|
||||||
|
#ifdef DEBUG
|
||||||
|
// Reset the backup to invalid values to detect unexpected usage
|
||||||
|
screen->windowed_window_size_backup.width = 0;
|
||||||
|
screen->windowed_window_size_backup.height = 0;
|
||||||
|
#endif
|
||||||
screen->maximized = true;
|
screen->maximized = true;
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_RESTORED:
|
case SDL_WINDOWEVENT_RESTORED:
|
||||||
screen->maximized = false;
|
screen->maximized = false;
|
||||||
apply_pending_resize(screen);
|
apply_windowed_size(screen);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,11 @@ struct screen {
|
|||||||
struct sc_opengl gl;
|
struct sc_opengl gl;
|
||||||
struct size frame_size;
|
struct size frame_size;
|
||||||
struct size content_size; // rotated frame_size
|
struct size content_size; // rotated frame_size
|
||||||
|
// The window size the last time it was not maximized or fullscreen.
|
||||||
bool resize_pending; // resize requested while fullscreen or maximized
|
struct size windowed_window_size;
|
||||||
// The content size the last time the window was not maximized or
|
// Since we receive the event SIZE_CHANGED before MAXIMIZED, we must be
|
||||||
// fullscreen (meaningful only when resize_pending is true)
|
// able to revert the size to its non-maximized value.
|
||||||
struct size windowed_content_size;
|
struct size windowed_window_size_backup;
|
||||||
|
|
||||||
// client rotation: 0, 1, 2 or 3 (x90 degrees counterclockwise)
|
// client rotation: 0, 1, 2 or 3 (x90 degrees counterclockwise)
|
||||||
unsigned rotation;
|
unsigned rotation;
|
||||||
bool has_frame;
|
bool has_frame;
|
||||||
@@ -50,8 +49,11 @@ struct screen {
|
|||||||
.width = 0, \
|
.width = 0, \
|
||||||
.height = 0, \
|
.height = 0, \
|
||||||
}, \
|
}, \
|
||||||
.resize_pending = false, \
|
.windowed_window_size = { \
|
||||||
.windowed_content_size = { \
|
.width = 0, \
|
||||||
|
.height = 0, \
|
||||||
|
}, \
|
||||||
|
.windowed_window_size_backup = { \
|
||||||
.width = 0, \
|
.width = 0, \
|
||||||
.height = 0, \
|
.height = 0, \
|
||||||
}, \
|
}, \
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2008, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@hide}
|
||||||
|
*/
|
||||||
|
oneway interface IOnPrimaryClipChangedListener {
|
||||||
|
void dispatchPrimaryClipChanged();
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import com.genymobile.scrcpy.wrappers.ServiceManager;
|
|||||||
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
||||||
import com.genymobile.scrcpy.wrappers.WindowManager;
|
import com.genymobile.scrcpy.wrappers.WindowManager;
|
||||||
|
|
||||||
|
import android.content.IOnPrimaryClipChangedListener;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@@ -66,6 +67,14 @@ public final class Device {
|
|||||||
}
|
}
|
||||||
}, displayId);
|
}, displayId);
|
||||||
|
|
||||||
|
serviceManager.getClipboardManager().addPrimaryClipChangedListener(new IOnPrimaryClipChangedListener.Stub() {
|
||||||
|
@Override
|
||||||
|
public void dispatchPrimaryClipChanged() {
|
||||||
|
String s = getClipboardText();
|
||||||
|
Ln.i("clipboard changed = " + s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if ((displayInfoFlags & DisplayInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS) == 0) {
|
if ((displayInfoFlags & DisplayInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS) == 0) {
|
||||||
Ln.w("Display doesn't have FLAG_SUPPORTS_PROTECTED_BUFFERS flag, mirroring can be restricted");
|
Ln.w("Display doesn't have FLAG_SUPPORTS_PROTECTED_BUFFERS flag, mirroring can be restricted");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static IBinder createDisplay() {
|
private static IBinder createDisplay() {
|
||||||
return SurfaceControl.createDisplay("scrcpy", true);
|
return SurfaceControl.createDisplay("scrcpy", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void configure(MediaCodec codec, MediaFormat format) {
|
private static void configure(MediaCodec codec, MediaFormat format) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.genymobile.scrcpy.wrappers;
|
|||||||
import com.genymobile.scrcpy.Ln;
|
import com.genymobile.scrcpy.Ln;
|
||||||
|
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
|
import android.content.IOnPrimaryClipChangedListener;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IInterface;
|
import android.os.IInterface;
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ public class ClipboardManager {
|
|||||||
private final IInterface manager;
|
private final IInterface manager;
|
||||||
private Method getPrimaryClipMethod;
|
private Method getPrimaryClipMethod;
|
||||||
private Method setPrimaryClipMethod;
|
private Method setPrimaryClipMethod;
|
||||||
|
private Method addPrimaryClipChangedListener;
|
||||||
|
|
||||||
public ClipboardManager(IInterface manager) {
|
public ClipboardManager(IInterface manager) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
@@ -81,4 +83,37 @@ public class ClipboardManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addPrimaryClipChangedListener(Method method, IInterface manager, IOnPrimaryClipChangedListener listener)
|
||||||
|
throws InvocationTargetException, IllegalAccessException {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
|
method.invoke(manager, listener, ServiceManager.PACKAGE_NAME);
|
||||||
|
} else {
|
||||||
|
method.invoke(manager, listener, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Method getAddPrimaryClipChangedListener() throws NoSuchMethodException {
|
||||||
|
if (addPrimaryClipChangedListener == null) {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
|
addPrimaryClipChangedListener = manager.getClass()
|
||||||
|
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class);
|
||||||
|
} else {
|
||||||
|
addPrimaryClipChangedListener = manager.getClass()
|
||||||
|
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, int.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addPrimaryClipChangedListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
|
||||||
|
try {
|
||||||
|
Method method = getAddPrimaryClipChangedListener();
|
||||||
|
addPrimaryClipChangedListener(method, manager, listener);
|
||||||
|
return true;
|
||||||
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
|
Ln.e("Could not invoke method", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user