Skip to content

Commit

Permalink
initial
Browse files Browse the repository at this point in the history
  • Loading branch information
azvegint committed Oct 3, 2023
1 parent b6a97c0 commit da78635
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@
package sun.awt.screencast;

import sun.awt.UNIXToolkit;
import sun.java2d.pipe.Region;
import sun.security.action.GetPropertyAction;

import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.geom.AffineTransform;
import java.security.AccessController;
import java.util.Arrays;
import java.util.List;
Expand Down Expand Up @@ -109,9 +112,20 @@ private static List<Rectangle> getSystemScreensBounds() {
.stream(GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getScreenDevices())
.map(graphicsDevice ->
graphicsDevice.getDefaultConfiguration().getBounds()
).toList();
.map(graphicsDevice -> {
GraphicsConfiguration gc =
graphicsDevice.getDefaultConfiguration();
Rectangle screen = gc.getBounds();
AffineTransform tx = gc.getDefaultTransform();

return new Rectangle(
Region.clipRound(screen.x * tx.getScaleX()),
Region.clipRound(screen.y * tx.getScaleY()),
Region.clipRound(screen.width * tx.getScaleX()),
Region.clipRound(screen.height * tx.getScaleY())
);
})
.toList();
}

private static synchronized native void closeSession();
Expand Down
11 changes: 11 additions & 0 deletions src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,17 @@ static Set<TokenItem> getTokens(List<Rectangle> affectedScreenBounds) {
System.out.println("// getTokens same sizes 2. " + result);
}

// 3. add tokens with the same or greater number of screens
// This is useful if we once received a token with one screen resolution
// and the same screen was later scaled in the system.
// In that case, the token is still valid.

allTokenItems
.stream()
.filter(t ->
t.allowedScreensBounds.size() >= affectedScreenBounds.size())
.forEach(result::add);

return result;
}

Expand Down
10 changes: 10 additions & 0 deletions src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,10 @@ GtkApi* gtk3_load(JNIEnv *env, const char* lib_name)

/* Pixbuf */
fp_gdk_pixbuf_new = dl_symbol("gdk_pixbuf_new");
fp_gdk_pixbuf_new_from_data = dl_symbol("gdk_pixbuf_new_from_data");
fp_gdk_pixbuf_scale_simple = dl_symbol("gdk_pixbuf_scale_simple");
fp_gdk_pixbuf_copy_area = dl_symbol("gdk_pixbuf_copy_area");

fp_gdk_pixbuf_new_from_file =
dl_symbol("gdk_pixbuf_new_from_file");
fp_gdk_pixbuf_get_from_drawable =
Expand Down Expand Up @@ -3123,4 +3127,10 @@ static void gtk3_init(GtkApi* gtk) {
gtk->g_main_context_iteration = fp_g_main_context_iteration;
gtk->g_error_free = fp_g_error_free;
gtk->g_unix_fd_list_get = fp_g_unix_fd_list_get;

gtk->gdk_pixbuf_new = fp_gdk_pixbuf_new;
gtk->gdk_pixbuf_new_from_data = fp_gdk_pixbuf_new_from_data;
gtk->gdk_pixbuf_scale_simple = fp_gdk_pixbuf_scale_simple;
gtk->gdk_pixbuf_get_pixels = fp_gdk_pixbuf_get_pixels;
gtk->gdk_pixbuf_copy_area = fp_gdk_pixbuf_copy_area;
}
24 changes: 24 additions & 0 deletions src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,30 @@ static void (*fp_gdk_draw_rectangle)(GdkDrawable*, GdkGC*, gboolean,
gint, gint, gint, gint);
static GdkPixbuf *(*fp_gdk_pixbuf_new)(GdkColorspace colorspace,
gboolean has_alpha, int bits_per_sample, int width, int height);

static GdkPixbuf *(*fp_gdk_pixbuf_new_from_data)(
const guchar *data,
GdkColorspace colorspace,
gboolean has_alpha,
int bits_per_sample,
int width,
int height,
int rowstride,
GdkPixbufDestroyNotify destroy_fn,
gpointer destroy_fn_data
);

static void (*fp_gdk_pixbuf_copy_area) (
const GdkPixbuf* src_pixbuf,
int src_x,
int src_y,
int width,
int height,
GdkPixbuf* dest_pixbuf,
int dest_x,
int dest_y
);

static void (*fp_gdk_drawable_get_size)(GdkDrawable *drawable,
gint* width, gint* height);
static gboolean (*fp_gtk_init_check)(int* argc, char** argv);
Expand Down
42 changes: 42 additions & 0 deletions src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,8 @@ typedef void (*GClosureNotify)(gpointer data, GClosure *closure);
typedef void (*GDestroyNotify)(gpointer data);
typedef void (*GCallback)(void);

typedef void GdkPixbuf;
typedef void (* GdkPixbufDestroyNotify) (guchar *pixels, gpointer data);

typedef struct GtkApi {
int version;
Expand Down Expand Up @@ -797,6 +799,46 @@ typedef struct GtkApi {
gint index_,
GError **error);

GdkPixbuf *(*gdk_pixbuf_new)(GdkColorspace colorspace,
gboolean has_alpha,
int bits_per_sample,
int width,
int height);


GdkPixbuf *(*gdk_pixbuf_new_from_data)(
const guchar *data,
GdkColorspace colorspace,
gboolean has_alpha,
int bits_per_sample,
int width,
int height,
int rowstride,
GdkPixbufDestroyNotify destroy_fn,
gpointer destroy_fn_data
);


GdkPixbuf *(*gdk_pixbuf_scale_simple)(GdkPixbuf *src,
int dest_width,
int dest_heigh,
GdkInterpType interp_type
);

guchar* (*gdk_pixbuf_get_pixels) (const GdkPixbuf* pixbuf);


void (*gdk_pixbuf_copy_area) (
const GdkPixbuf* src_pixbuf,
int src_x,
int src_y,
int width,
int height,
GdkPixbuf* dest_pixbuf,
int dest_x,
int dest_y
);

/* </for screencast, used only with GTK3> */
} GtkApi;

Expand Down
138 changes: 76 additions & 62 deletions src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,49 +175,6 @@ static gboolean initScreencast(const gchar *token,
return TRUE;
}

static inline void convertRGBxToBGRx(int* in) {
char* o = (char*) in;
char tmp = o[0];
o[0] = o[2];
o[2] = tmp;
}

static gchar * cropTo(
struct spa_data data,
struct spa_video_info_raw raw,
guint32 x,
guint32 y,
guint32 width,
guint32 height
) {
int srcW = raw.size.width;
if (data.chunk->stride / 4 != srcW) {
fprintf(stderr, "%s:%i Unexpected stride / 4: %i srcW: %i\n",
__func__, __LINE__, data.chunk->stride / 4, srcW);
}

int* d = data.data;

int *outData = calloc(width * height, sizeof(int));
if (!outData) {
ERR("failed to allocate memory\n");
return NULL;
}

gboolean needConversion = raw.format != SPA_VIDEO_FORMAT_BGRx;
for (guint32 j = y; j < y + height; ++j) {
for (guint32 i = x; i < x + width; ++i) {
int color = *(d + (j * srcW) + i);
if (needConversion) {
convertRGBxToBGRx(&color);
}
*(outData + ((j - y) * width) + (i - x)) = color;
}
}

return (gchar*) outData;
}

static void onStreamParamChanged(
void *userdata,
uint32_t id,
Expand Down Expand Up @@ -301,24 +258,81 @@ static void onStreamProcess(void *userdata) {

struct spa_data spaData = spaBuffer->datas[0];

gint streamWidth = data->rawFormat.size.width;
gint streamHeight = data->rawFormat.size.height;

DEBUG_SCREEN(screen);
DEBUG_SCREEN_PREFIX(screen,
"got a frame of size %d offset %d stride %d "
"flags %d FD %li captureDataReady %i\n",
"flags %d FD %li captureDataReady %i of stream %dx%d\n",
spaBuffer->datas[0].chunk->size,
spaData.chunk->offset,
spaData.chunk->stride,
spaData.chunk->flags,
spaData.fd,
screen->captureDataReady
screen->captureDataReady,
streamWidth,
streamHeight
);

data->screenProps->captureData = cropTo(
spaData,
data->rawFormat,
screen->captureArea.x, screen->captureArea.y,
screen->captureArea.width, screen->captureArea.height
);
GdkRectangle captureArea = screen->captureArea;
GdkRectangle screenBounds = screen->bounds;

GdkPixbuf *pixbuf = gtk->gdk_pixbuf_new_from_data(spaData.data,
GDK_COLORSPACE_RGB,
TRUE,
8,
streamWidth,
streamHeight,
spaData.chunk->stride,
NULL,
NULL);

if (screen->bounds.width != streamWidth
|| screen->bounds.height != streamHeight) {

DEBUG_SCREEN_PREFIX(screen, "scaling stream data %dx%d -> %dx%d\n",
streamWidth, streamHeight,
screen->bounds.width, screen->bounds.height
);

GdkPixbuf *scaled = gtk->gdk_pixbuf_scale_simple(pixbuf,
screen->bounds.width,
screen->bounds.height,
GDK_INTERP_BILINEAR);

gtk->g_object_unref(pixbuf);
pixbuf = scaled;
}

GdkPixbuf *cropped = NULL;
if (captureArea.width != screenBounds.width
|| captureArea.height != screenBounds.height) {

cropped = gtk->gdk_pixbuf_new(GDK_COLORSPACE_RGB,
TRUE,
8,
captureArea.width,
captureArea.height);
if (cropped) {
gtk->gdk_pixbuf_copy_area(pixbuf,
captureArea.x,
captureArea.y,
captureArea.width,
captureArea.height,
cropped,
0, 0);
} else {
ERR("Cannot create a new pixbuf.\n");
}

gtk->g_object_unref(pixbuf);
pixbuf = NULL;

data->screenProps->captureDataPixbuf = cropped;
} else {
data->screenProps->captureDataPixbuf = pixbuf;
}

screen->captureDataReady = TRUE;

Expand Down Expand Up @@ -366,11 +380,7 @@ static bool startStream(
SPA_FORMAT_mediaSubtype,
SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
SPA_FORMAT_VIDEO_format,
SPA_POD_CHOICE_ENUM_Id(
2,
SPA_VIDEO_FORMAT_RGBx,
SPA_VIDEO_FORMAT_BGRx
),
SPA_POD_Id(SPA_VIDEO_FORMAT_BGRx),
SPA_FORMAT_VIDEO_size,
SPA_POD_CHOICE_RANGE_Rectangle(
&SPA_RECTANGLE(320, 240),
Expand Down Expand Up @@ -910,7 +920,7 @@ JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_getRGBPixelsImpl
"\t||\tx %5i y %5i w %5i h %5i %s\n"
"\t||\tx %5i y %5i w %5i h %5i %s\n"
"\t||\tx %5i y %5i w %5i h %5i %s\n\n",
i, screenProps->captureData,
i, screenProps->captureDataPixbuf,
requestedArea.x, requestedArea.y,
requestedArea.width, requestedArea.height,
"requested area",
Expand All @@ -924,7 +934,7 @@ JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_getRGBPixelsImpl
"in-screen coords capture area"
);

if (screenProps->captureData) {
if (screenProps->captureDataPixbuf) {
for (int y = 0; y < captureArea.height; y++) {
jsize preY = (requestedArea.y > screenProps->bounds.y)
? 0
Expand All @@ -939,14 +949,18 @@ JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_getRGBPixelsImpl
(*env)->SetIntArrayRegion(
env, pixelArray,
start, len,
((jint *) screenProps->captureData)
+ (captureArea.width * y)
((jint *) gtk->gdk_pixbuf_get_pixels(
screenProps->captureDataPixbuf
))
+ (captureArea.width * y)
);
}
}

free(screenProps->captureData);
screenProps->captureData = NULL;
if (screenProps->captureDataPixbuf) {
gtk->g_object_unref(screenProps->captureDataPixbuf);
screenProps->captureDataPixbuf = NULL;
}
screenProps->shouldCapture = FALSE;

fp_pw_thread_loop_lock(pw.loop);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ struct ScreenProps {
GdkRectangle captureArea;
struct PwStreamData *data;

gchar *captureData;
GdkPixbuf *captureDataPixbuf;
volatile gboolean shouldCapture;
volatile gboolean captureDataReady;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import javax.swing.UIManager;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
Expand All @@ -54,6 +53,12 @@ public class ScreenCaptureGtkTest {
Color.GREEN, Color.BLUE, Color.ORANGE, Color.RED};

public static void main(String[] args) throws Exception {
if ("2".equals(System.getProperty("jdk.gtk.version"))
&& System.getenv("WAYLAND_DISPLAY") != null) {
// screen capture is not supported with gtk2 on Wayland
return;
}

final int topOffset = 50;
final int leftOffset = 50;

Expand Down

0 comments on commit da78635

Please sign in to comment.