broadway: Use textures to configure window contents

Instead of using the old buffer code, just use textures to define the
window contents.
This commit is contained in:
Alexander Larsson 2017-11-18 20:36:57 +01:00
parent a4636a06a7
commit cc7423855b
15 changed files with 79 additions and 1085 deletions

View File

@ -1,528 +0,0 @@
#include "config.h"
#include "broadway-buffer.h"
#include <string.h>
/* This code is based on some code from weston with this license:
*
* Copyright © 2012 Intel Corporation
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
struct entry {
int count;
int matches;
guint32 hash;
int x, y;
int index;
};
struct _BroadwayBuffer {
guint8 *data;
struct entry *table;
int width, height, stride;
int encoded;
int block_stride, length, block_count, shift;
int stats[5];
int clashes;
};
static const guint32 prime = 0x1f821e2d;
static const guint32 end_prime = 0xf907ec81; /* prime^block_size */
#if 0
static const guint32 vprime = 0x0137b89d;
static const guint32 end_vprime = 0xaea9a281; /* vprime^block_size */
#else
static const guint32 vprime = 0xf907ec81;
static const guint32 end_vprime = 0xcdb99001; /* vprime^block_size */
#endif
static const guint32 step = 0x0ac93019;
static const int block_size = 32, block_mask = 31;
static gboolean
verify_block_match (BroadwayBuffer *buffer, int x, int y,
BroadwayBuffer *prev, struct entry *entry)
{
int i;
void *old, *match;
int w1, w2, h1, h2;
w1 = block_size;
if (x + block_size > buffer->width)
w1 = buffer->width - x;
h1 = block_size;
if (y + block_size > buffer->height)
h1 = buffer->height - y;
w2 = block_size;
if (entry->x + block_size > prev->width)
w2 = prev->width - entry->x;
h2 = block_size;
if (entry->y + block_size > prev->height)
h2 = prev->height - entry->y;
if (w1 != w2 || h1 != h2)
return FALSE;
for (i = 0; i < h1; i++)
{
match = buffer->data + (y + i) * buffer->stride + x * 4;
old = prev->data + (entry->y + i) * prev->stride + entry->x * 4;
if (memcmp (match, old, w1 * 4) != 0)
{
buffer->clashes++;
return FALSE;
}
}
return TRUE;
}
static void
insert_block (BroadwayBuffer *buffer, guint32 h, int x, int y)
{
struct entry *entry;
int i;
guint32 collision = 0;
entry = &buffer->table[h >> buffer->shift];
for (i = step; entry->count > 0 && entry->hash != h; i += step)
{
entry = &buffer->table[(h + i) >> buffer->shift];
collision++;
}
entry->hash = h;
entry->count++;
entry->x = x;
entry->y = y;
entry->index = (buffer->block_stride * y + x) / block_size;
if (collision > G_N_ELEMENTS (buffer->stats) - 1)
collision = G_N_ELEMENTS (buffer->stats) - 1;
buffer->stats[collision]++;
}
static struct entry *
lookup_block (BroadwayBuffer *prev, guint32 h)
{
guint32 i;
struct entry *entry;
int shift = prev->shift;
for (i = h;
entry = &prev->table[i >> shift], entry->count > 0;
i += step)
{
if (entry->hash == h)
return entry;
}
return NULL;
}
struct encoder {
guint32 color;
guint32 color_run;
guint32 delta;
guint32 delta_run;
GString *dest;
int bytes;
};
/* Encoding:
*
* - all 1 pixel colors are encoded literally
*
* - We dont need to support colors with alpha 0 and non-zero
* color components, as they mean the same on the canvas anyway.
* So we use these as special codes:
*
* - 0x00 00 00 00 : one alpha 0 pixel
* - 0xaa rr gg bb : one color pixel, alpha > 0
* - 0x00 1x xx xx : delta 0 run, x is length, (20 bits)
* - 0x00 2x xx xx 0x xxxx yyyy: block ref, block number x (20 bits) at x, y
* - 0x00 3x xx xx 0xaarrggbb : solid color run, length x
* - 0x00 4x xx xx 0xaarrggbb : delta run, length x
*
*/
static void
emit (struct encoder *encoder, guint32 symbol)
{
g_string_append_len (encoder->dest, (char *)&symbol, sizeof (guint32));
encoder->bytes += sizeof (guint32);
}
static void
encode_run (struct encoder *encoder)
{
if (encoder->color_run == 0 && encoder->delta_run == 0)
return;
if (encoder->color_run >= encoder->delta_run)
{
if (encoder->color_run == 1)
emit (encoder, encoder->color);
else
{
emit (encoder, 0x00300000 | encoder->color_run);
emit (encoder, encoder->color);
}
}
else
{
if (encoder->delta == 0)
emit(encoder, 0x00100000 | encoder->delta_run);
else
{
emit(encoder, 0x00400000 | encoder->delta_run);
emit(encoder, encoder->delta);
}
}
}
static void
encode_pixel (struct encoder *encoder, guint32 color, guint32 prev_color)
{
guint32 delta = 0;
guint32 a, r, g, b;
if (color == prev_color)
delta = 0;
else if (prev_color == 0)
delta = color;
else
{
a = ((color & 0xff000000) - (prev_color & 0xff000000)) & 0xff000000;
r = ((color & 0x00ff0000) - (prev_color & 0x00ff0000)) & 0x00ff0000;
g = ((color & 0x0000ff00) - (prev_color & 0x0000ff00)) & 0x0000ff00;
b = ((color & 0x000000ff) - (prev_color & 0x000000ff)) & 0x000000ff;
delta = a | r | g | b;
}
if ((encoder->color != color &&
encoder->color_run > encoder->delta_run) ||
(encoder->delta != delta &&
encoder->delta_run > encoder->color_run) ||
(encoder->delta != delta && encoder->color != color) ||
(encoder->delta_run == 0xFFFFF || encoder->color_run == 0xFFFFF))
{
encode_run (encoder);
encoder->color_run = 1;
encoder->color = color;
encoder->delta_run = 1;
encoder->delta = delta;
return;
}
if (encoder->color == color)
encoder->color_run++;
else
{
encoder->color_run = 1;
encoder->color = color;
}
if (encoder->delta == delta)
encoder->delta_run++;
else
{
encoder->delta_run = 1;
encoder->delta = delta;
}
}
static void
encoder_flush (struct encoder *encoder)
{
encode_run (encoder);
}
static void
encode_block (struct encoder *encoder, struct entry *entry, int x, int y)
{
/* 0x00 2x xx xx 0x xxxx yyyy:
* block ref, block number x (20 bits) at x, y */
/* FIXME: Maybe don't encode pixels under blocks and just emit
* blocks at their position within the stream. */
emit (encoder, 0x00200000 | entry->index);
emit (encoder, (x << 16) | y);
}
void
broadway_buffer_destroy (BroadwayBuffer *buffer)
{
g_free (buffer->data);
g_free (buffer->table);
g_free (buffer);
}
int
broadway_buffer_get_width (BroadwayBuffer *buffer)
{
return buffer->width;
}
int
broadway_buffer_get_height (BroadwayBuffer *buffer)
{
return buffer->height;
}
static void
unpremultiply_line (void *destp, void *srcp, int width)
{
guint32 *src = srcp;
guint32 *dest = destp;
guint32 *end = src + width;
while (src < end)
{
guint32 pixel;
guint8 alpha, r, g, b;
pixel = *src++;
alpha = (pixel & 0xff000000) >> 24;
if (alpha == 0xff)
*dest++ = pixel;
else if (alpha == 0)
*dest++ = 0;
else
{
r = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
g = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
b = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
*dest++ = (guint32)alpha << 24 | (guint32)r << 16 | (guint32)g << 8 | (guint32)b;
}
}
}
BroadwayBuffer *
broadway_buffer_create (int width, int height, guint8 *data, int stride)
{
BroadwayBuffer *buffer;
int y, bits_required;
buffer = g_new0 (BroadwayBuffer, 1);
buffer->width = width;
buffer->stride = width * 4;
buffer->height = height;
buffer->block_stride = (width + block_size - 1) / block_size;
buffer->block_count =
buffer->block_stride * ((height + block_size - 1) / block_size);
bits_required = g_bit_storage (buffer->block_count * 4);
buffer->shift = 32 - bits_required;
buffer->length = 1 << bits_required;
buffer->table = g_malloc0 (buffer->length * sizeof buffer->table[0]);
memset (buffer->stats, 0, sizeof buffer->stats);
buffer->clashes = 0;
buffer->data = g_malloc (buffer->stride * height);
for (y = 0; y < height; y++)
unpremultiply_line (buffer->data + y * buffer->stride, data + y * stride, width);
return buffer;
}
void
broadway_buffer_encode (BroadwayBuffer *buffer, BroadwayBuffer *prev, GString *dest)
{
struct entry *entry;
int i, j, k;
int x0, x1, y0, y1;
guint32 *block_hashes;
guint32 hash, bottom_hash, h, *line, *bottom, *prev_line;
int width, height;
struct encoder encoder = { 0 };
int *skyline, skyline_pixels;
int matches;
width = buffer->width;
height = buffer->height;
x0 = 0;
x1 = width;
y0 = 0;
y1 = height;
skyline = g_malloc0 ((width + block_size) * sizeof skyline[0]);
block_hashes = g_malloc0 (width * sizeof block_hashes[0]);
matches = 0;
encoder.dest = dest;
// Calculate the block hashes for the first row
for (i = y0; i < MIN(y1, y0 + block_size); i++)
{
line = (guint32 *)(buffer->data + i * buffer->stride);
hash = 0;
for (j = x0; j < MIN(x1, x0 + block_size); j++)
hash = hash * prime + line[j];
for (; j < x0 + block_size; j++)
hash = hash * prime;
for (j = x0; j < x1; j++)
{
block_hashes[j] = block_hashes[j] * vprime + hash;
hash = hash * prime - line[j] * end_prime;
if (j + block_size < width)
hash += line[j + block_size];
}
}
// Do the last rows if height < block_size
for (; i < y0 + block_size; i++)
{
for (j = x0; j < x1; j++)
block_hashes[j] = block_hashes[j] * vprime;
}
for (i = y0; i < y1; i++)
{
line = (guint32 *) (buffer->data + i * buffer->stride);
bottom = (guint32 *) (buffer->data + (i + block_size) * buffer->stride);
bottom_hash = 0;
hash = 0;
skyline_pixels = 0;
if (prev && i < prev->height)
prev_line = (guint32 *) (prev->data + i * prev->stride);
else
prev_line = NULL;
for (j = x0; j < x0 + block_size; j++)
{
hash = hash * prime;
if (j < width)
hash += line[j];
if (i + block_size < height)
{
bottom_hash = bottom_hash * prime;
if (j < width)
bottom_hash += bottom[j];
}
if (i < skyline[j])
skyline_pixels = 0;
else
skyline_pixels++;
}
for (j = x0; j < x1; j++)
{
if (i < skyline[j])
encode_pixel (&encoder, line[j], line[j]);
else if (prev)
{
/* FIXME: Add back overlap exception
* for consecutive blocks */
h = block_hashes[j];
entry = lookup_block (prev, h);
if (entry && entry->count < 2 &&
skyline_pixels >= block_size &&
verify_block_match (buffer, j, i, prev, entry) &&
(entry->x != j || entry->y != i))
{
matches++;
encode_block (&encoder, entry, j, i);
for (k = 0; k < block_size; k++)
skyline[j + k] = i + block_size;
encode_pixel (&encoder, line[j], line[j]);
}
else
{
if (prev_line && j < prev->width)
encode_pixel (&encoder, line[j],
prev_line[j]);
else
encode_pixel (&encoder, line[j], 0);
}
}
else
encode_pixel (&encoder, line[j], 0);
if (i < skyline[j + block_size])
skyline_pixels = 0;
else
skyline_pixels++;
/* Insert block in hash table if we're on a
* grid point. */
if (((i | j) & block_mask) == 0 && !buffer->encoded)
insert_block (buffer, block_hashes[j], j, i);
/* Update sliding block hash */
block_hashes[j] =
block_hashes[j] * vprime + bottom_hash -
hash * end_vprime;
if (i + block_size < height)
{
bottom_hash = bottom_hash * prime - bottom[j] * end_prime;
if (j + block_size < width)
bottom_hash += bottom[j + block_size];
}
hash = hash * prime - line[j] * end_prime;
if (j + block_size < width)
hash += line[j + block_size] ;
}
}
encoder_flush (&encoder);
#if 0
fprintf(stderr, "collision stats:");
for (i = 0; i < (int) G_N_ELEMENTS(buffer->stats); i++)
fprintf(stderr, "%c%d", i == 0 ? ' ' : '/', buffer->stats[i]);
fprintf(stderr, "\n");
fprintf(stderr, "%d / %d blocks (%d%%) matched, %d clashes\n",
matches, buffer->block_count,
100 * matches / buffer->block_count, buffer->clashes);
fprintf(stderr, "output stream %d bytes, raw buffer %d bytes (%d%%)\n",
encoder.bytes, height * buffer->stride,
100 * encoder.bytes / (height * buffer->stride));
#endif
g_free (skyline);
g_free (block_hashes);
buffer->encoded = TRUE;
}

View File

@ -1,20 +0,0 @@
#ifndef __BROADWAY_BUFFER__
#define __BROADWAY_BUFFER__
#include "broadway-protocol.h"
#include <glib-object.h>
typedef struct _BroadwayBuffer BroadwayBuffer;
BroadwayBuffer *broadway_buffer_create (int width,
int height,
guint8 *data,
int stride);
void broadway_buffer_destroy (BroadwayBuffer *buffer);
void broadway_buffer_encode (BroadwayBuffer *buffer,
BroadwayBuffer *prev,
GString *dest);
int broadway_buffer_get_width (BroadwayBuffer *buffer);
int broadway_buffer_get_height (BroadwayBuffer *buffer);
#endif /* __BROADWAY_BUFFER__ */

View File

@ -287,48 +287,14 @@ broadway_output_set_transient_for (BroadwayOutput *output,
}
void
broadway_output_put_buffer (BroadwayOutput *output,
broadway_output_window_update (BroadwayOutput *output,
int id,
BroadwayBuffer *prev_buffer,
BroadwayBuffer *buffer)
guint32 texture)
{
gsize len;
int w, h;
GZlibCompressor *compressor;
GOutputStream *out, *out_mem;
GString *encoded;
write_header (output, BROADWAY_OP_PUT_BUFFER);
w = broadway_buffer_get_width (buffer);
h = broadway_buffer_get_height (buffer);
write_header (output, BROADWAY_OP_WINDOW_UPDATE);
append_uint16 (output, id);
append_uint16 (output, w);
append_uint16 (output, h);
encoded = g_string_new ("");
broadway_buffer_encode (buffer, prev_buffer, encoded);
compressor = g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_RAW, -1);
out_mem = g_memory_output_stream_new_resizable ();
out = g_converter_output_stream_new (out_mem, G_CONVERTER (compressor));
g_object_unref (compressor);
if (!g_output_stream_write_all (out, encoded->str, encoded->len,
NULL, NULL, NULL) ||
!g_output_stream_close (out, NULL, NULL))
g_warning ("compression failed");
len = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (out_mem));
append_uint32 (output, len);
g_string_append_len (output->buf, g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (out_mem)), len);
g_string_free (encoded, TRUE);
g_object_unref (out);
g_object_unref (out_mem);
append_uint32 (output, texture);
}
void

View File

@ -4,7 +4,6 @@
#include <glib.h>
#include <gio/gio.h>
#include "broadway-protocol.h"
#include "broadway-buffer.h"
typedef struct BroadwayOutput BroadwayOutput;
@ -54,10 +53,9 @@ void broadway_output_move_resize_surface (BroadwayOutput *output,
void broadway_output_set_transient_for (BroadwayOutput *output,
int id,
int parent_id);
void broadway_output_put_buffer (BroadwayOutput *output,
void broadway_output_window_update (BroadwayOutput *output,
int id,
BroadwayBuffer *prev_buffer,
BroadwayBuffer *buffer);
guint32 texture);
void broadway_output_upload_texture (BroadwayOutput *output,
guint32 id,
GBytes *texture);

View File

@ -41,7 +41,7 @@ typedef enum {
BROADWAY_OP_REQUEST_AUTH = 'l',
BROADWAY_OP_AUTH_OK = 'L',
BROADWAY_OP_DISCONNECTED = 'D',
BROADWAY_OP_PUT_BUFFER = 'b',
BROADWAY_OP_WINDOW_UPDATE = 'b',
BROADWAY_OP_SET_SHOW_KEYBOARD = 'k',
BROADWAY_OP_UPLOAD_TEXTURE = 't',
BROADWAY_OP_RELEASE_TEXTURE = 'T',
@ -193,9 +193,7 @@ typedef struct {
typedef struct {
BroadwayRequestBase base;
guint32 id;
char name[36];
guint32 width;
guint32 height;
guint32 texture;
} BroadwayRequestUpdate;
typedef struct {

View File

@ -116,12 +116,7 @@ struct BroadwayWindow {
gboolean is_temp;
gboolean visible;
gint32 transient_for;
BroadwayBuffer *buffer;
gboolean buffer_synced;
char *cached_surface_name;
cairo_surface_t *cached_surface;
guint32 texture;
};
static void broadway_server_resync_windows (BroadwayServer *server);
@ -826,87 +821,6 @@ broadway_server_block_for_input (BroadwayServer *server, char op,
}
#endif
static void *
map_named_shm (char *name, gsize size)
{
#ifdef G_OS_UNIX
int fd;
void *ptr;
char *filename = NULL;
fd = shm_open (name, O_RDONLY, 0600);
if (fd == -1)
{
filename = g_build_filename (g_get_tmp_dir (), name, NULL);
fd = open (filename, O_RDONLY);
if (fd == -1)
{
perror ("Failed to map shm");
g_free (filename);
return NULL;
}
}
ptr = mmap (0, size, PROT_READ, MAP_SHARED, fd, 0);
(void) close (fd);
if (filename)
{
unlink (filename);
g_free (filename);
}
else
shm_unlink (name);
return ptr;
#elif defined(G_OS_WIN32)
int fd;
void *ptr;
char *shmpath;
void *map = ((void *)-1);
if (*name == '/')
++name;
shmpath = g_build_filename (g_get_tmp_dir (), name, NULL);
fd = open(shmpath, O_RDONLY, 0600);
if (fd == -1)
{
g_free (shmpath);
perror ("Failed to shm_open");
return NULL;
}
if (size == 0)
ptr = map;
else
{
HANDLE h, fm;
h = (HANDLE)_get_osfhandle (fd);
fm = CreateFileMapping (h, NULL, PAGE_READONLY, 0, (DWORD)size, NULL);
ptr = MapViewOfFile (fm, FILE_MAP_READ, 0, 0, (size_t)size);
CloseHandle (fm);
}
(void) close(fd);
remove (shmpath);
g_free (shmpath);
return ptr;
#else
#error "No shm mapping supported"
return NULL;
#endif
}
static const char *
parse_line (const char *line, const char *key)
{
@ -1463,10 +1377,6 @@ broadway_server_destroy_window (BroadwayServer *server,
g_hash_table_remove (server->id_ht,
GINT_TO_POINTER (id));
g_free (window->cached_surface_name);
if (window->cached_surface != NULL)
cairo_surface_destroy (window->cached_surface);
g_free (window);
}
}
@ -1603,37 +1513,20 @@ broadway_server_has_client (BroadwayServer *server)
void
broadway_server_window_update (BroadwayServer *server,
gint id,
cairo_surface_t *surface)
guint32 texture)
{
BroadwayWindow *window;
BroadwayBuffer *buffer;
if (surface == NULL)
return;
window = g_hash_table_lookup (server->id_ht,
GINT_TO_POINTER (id));
if (window == NULL)
return;
g_assert (window->width == cairo_image_surface_get_width (surface));
g_assert (window->height == cairo_image_surface_get_height (surface));
buffer = broadway_buffer_create (window->width, window->height,
cairo_image_surface_get_data (surface),
cairo_image_surface_get_stride (surface));
window->texture = texture;
if (server->output != NULL)
{
window->buffer_synced = TRUE;
broadway_output_put_buffer (server->output, window->id,
window->buffer, buffer);
}
if (window->buffer)
broadway_buffer_destroy (window->buffer);
window->buffer = buffer;
broadway_output_window_update (server->output, window->id,
window->texture);
}
guint32
@ -1788,78 +1681,6 @@ broadway_server_ungrab_pointer (BroadwayServer *server,
return serial;
}
static const cairo_user_data_key_t shm_cairo_key;
typedef struct {
void *data;
gsize data_size;
} ShmSurfaceData;
static void
shm_data_unmap (void *_data)
{
ShmSurfaceData *data = _data;
#ifdef G_OS_UNIX
munmap (data->data, data->data_size);
#elif defined(G_OS_WIN32)
UnmapViewOfFile (data->data);
#endif
g_free (data);
}
cairo_surface_t *
broadway_server_open_surface (BroadwayServer *server,
guint32 id,
char *name,
int width,
int height)
{
BroadwayWindow *window;
ShmSurfaceData *data;
cairo_surface_t *surface;
gsize size;
void *ptr;
window = g_hash_table_lookup (server->id_ht,
GINT_TO_POINTER (id));
if (window == NULL)
return NULL;
if (window->cached_surface_name != NULL &&
strcmp (name, window->cached_surface_name) == 0)
return cairo_surface_reference (window->cached_surface);
size = width * height * sizeof (guint32);
ptr = map_named_shm (name, size);
if (ptr == NULL)
return NULL;
data = g_new0 (ShmSurfaceData, 1);
data->data = ptr;
data->data_size = size;
surface = cairo_image_surface_create_for_data ((guchar *)data->data,
CAIRO_FORMAT_ARGB32,
width, height,
width * sizeof (guint32));
g_assert (surface != NULL);
cairo_surface_set_user_data (surface, &shm_cairo_key,
data, shm_data_unmap);
g_free (window->cached_surface_name);
window->cached_surface_name = g_strdup (name);
if (window->cached_surface != NULL)
cairo_surface_destroy (window->cached_surface);
window->cached_surface = cairo_surface_reference (surface);
return surface;
}
guint32
broadway_server_new_window (BroadwayServer *server,
int x,
@ -1929,7 +1750,6 @@ broadway_server_resync_windows (BroadwayServer *server)
if (window->id == 0)
continue; /* Skip root */
window->buffer_synced = FALSE;
broadway_output_new_surface (server->output,
window->id,
window->x,
@ -1948,18 +1768,14 @@ broadway_server_resync_windows (BroadwayServer *server)
continue; /* Skip root */
if (window->transient_for != -1)
broadway_output_set_transient_for (server->output, window->id, window->transient_for);
if (window->visible)
{
broadway_output_show_surface (server->output, window->id);
broadway_output_set_transient_for (server->output, window->id,
window->transient_for);
if (window->buffer != NULL)
{
window->buffer_synced = TRUE;
broadway_output_put_buffer (server->output, window->id,
NULL, window->buffer);
}
}
broadway_output_window_update (server->output, window->id,
window->texture);
if (window->visible)
broadway_output_show_surface (server->output, window->id);
}
if (server->show_keyboard)

View File

@ -84,7 +84,7 @@ cairo_surface_t * broadway_server_create_surface (int
int height);
void broadway_server_window_update (BroadwayServer *server,
gint id,
cairo_surface_t *surface);
guint32 texture);
gboolean broadway_server_window_move_resize (BroadwayServer *server,
gint id,
gboolean with_move,
@ -94,10 +94,5 @@ gboolean broadway_server_window_move_resize (BroadwayServer *
int height);
void broadway_server_focus_window (BroadwayServer *server,
gint new_focused_window);
cairo_surface_t * broadway_server_open_surface (BroadwayServer *server,
guint32 id,
char *name,
int width,
int height);
#endif /* __BROADWAY_SERVER__ */

View File

@ -67,25 +67,6 @@ function logStackTrace(len) {
log(callstack[i]);
}
function resizeCanvas(canvas, w, h)
{
/* Canvas resize clears the data, so we need to save it first */
var tmpCanvas = canvas.ownerDocument.createElement("canvas");
tmpCanvas.width = canvas.width;
tmpCanvas.height = canvas.height;
var tmpContext = tmpCanvas.getContext("2d");
tmpContext.globalCompositeOperation = "copy";
tmpContext.drawImage(canvas, 0, 0, tmpCanvas.width, tmpCanvas.height);
canvas.width = w;
canvas.height = h;
var context = canvas.getContext("2d");
context.globalCompositeOperation = "copy";
context.drawImage(tmpCanvas, 0, 0, tmpCanvas.width, tmpCanvas.height);
}
var grab = new Object();
grab.window = null;
grab.ownerEvents = false;
@ -160,15 +141,15 @@ function cmdCreateSurface(id, x, y, width, height, isTemp)
surface.visible = false;
surface.imageData = null;
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
canvas.surface = surface;
surface.canvas = canvas;
var image = new Image();
image.width = width;
image.height = height;
image.surface = surface;
surface.image = image;
var toplevelElement;
toplevelElement = canvas;
document.body.appendChild(canvas);
toplevelElement = image;
document.body.appendChild(image);
surface.toplevelElement = toplevelElement;
toplevelElement.style["position"] = "absolute";
@ -268,8 +249,8 @@ function cmdDeleteSurface(id)
var i = stackingOrder.indexOf(surface);
if (i >= 0)
stackingOrder.splice(i, 1);
var canvas = surface.canvas;
canvas.parentNode.removeChild(canvas);
var image = surface.image;
image.parentNode.removeChild(image);
delete surfaces[id];
}
@ -286,15 +267,17 @@ function cmdMoveResizeSurface(id, has_pos, x, y, has_size, w, h)
surface.height = h;
}
if (has_size)
resizeCanvas(surface.canvas, w, h);
if (has_size) {
surface.image.width = w;
surface.image.height = h;
}
if (surface.visible) {
if (has_pos) {
var xOffset = surface.x;
var yOffset = surface.y;
var element = surface.canvas;
var element = surface.image;
element.style["left"] = xOffset + "px";
element.style["top"] = yOffset + "px";
@ -512,21 +495,12 @@ function decodeBuffer(context, oldData, w, h, data, debug)
return imageData;
}
function cmdPutBuffer(id, w, h, compressed)
function cmdWindowUpdate(id, texture_id)
{
var surface = surfaces[id];
var context = surface.canvas.getContext("2d");
var texture_url = textures[texture];
var inflate = new Zlib.RawInflate(compressed);
var data = inflate.decompress();
var imageData = decodeBuffer (context, surface.imageData, w, h, data, debugDecoding);
context.putImageData(imageData, 0, 0);
if (debugDecoding)
imageData = decodeBuffer (context, surface.imageData, w, h, data, false);
surface.imageData = imageData;
surface.image.src = texture_url;
}
function cmdUploadTexture(id, data)
@ -631,12 +605,10 @@ function handleCommands(cmd)
cmdLowerSurface(id);
break;
case 'b': // Put image buffer
case 'b': // Update window
id = cmd.get_16();
w = cmd.get_16();
h = cmd.get_16();
var data = cmd.get_data();
cmdPutBuffer(id, w, h, data);
texture = cmd.get_32();
cmdWindowUpdate(id, texture);
break;
case 't': // Upload texture

View File

@ -225,7 +225,6 @@ client_handle_request (BroadwayClient *client,
BroadwayReplyQueryMouse reply_query_mouse;
BroadwayReplyGrabPointer reply_grab_pointer;
BroadwayReplyUngrabPointer reply_ungrab_pointer;
cairo_surface_t *surface;
guint32 before_serial, now_serial;
guint32 global_id;
int fd;
@ -284,18 +283,11 @@ client_handle_request (BroadwayClient *client,
request->set_transient_for.parent);
break;
case BROADWAY_REQUEST_UPDATE:
surface = broadway_server_open_surface (server,
request->update.id,
request->update.name,
request->update.width,
request->update.height);
if (surface != NULL)
{
global_id = GPOINTER_TO_INT (g_hash_table_lookup (client->textures,
GINT_TO_POINTER (request->update.texture)));
broadway_server_window_update (server,
request->update.id,
surface);
cairo_surface_destroy (surface);
}
global_id);
break;
case BROADWAY_REQUEST_UPLOAD_TEXTURE:
if (client->fds == NULL)

View File

@ -552,229 +552,15 @@ _gdk_broadway_server_window_set_transient_for (GdkBroadwayServer *server,
BROADWAY_REQUEST_SET_TRANSIENT_FOR);
}
static void *
map_named_shm (char *name, gsize size, gboolean *is_shm)
{
#ifdef G_OS_UNIX
char *filename = NULL;
int fd;
void *ptr;
int res;
fd = shm_open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
if (fd == -1)
{
if (errno == EEXIST)
return NULL;
filename = g_build_filename (g_get_tmp_dir (), name, NULL);
fd = open (filename, O_RDWR | O_CREAT | O_EXCL, 0600);
g_free (filename);
if (fd == -1)
{
if (errno != EEXIST)
g_error ("Unable to allocate shared mem for window");
return NULL;
}
else
*is_shm = FALSE;
}
else
*is_shm = TRUE;
res = ftruncate (fd, size);
g_assert (res != -1);
#ifdef HAVE_POSIX_FALLOCATE
res = posix_fallocate (fd, 0, size);
if (res != 0 && errno == ENOSPC)
{
if (filename)
unlink (filename);
else
shm_unlink (name);
g_error ("Not enough shared memory for window surface");
}
#endif
ptr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
(void) close(fd);
return ptr;
#elif defined(G_OS_WIN32)
int fd;
void *ptr;
char *shmpath;
void *map = ((void *)-1);
int res;
if (*name == '/')
++name;
shmpath = g_build_filename (g_get_tmp_dir (), name, NULL);
fd = open(shmpath, O_RDWR|O_CREAT|O_EXCL, 0600);
g_free (shmpath);
if (fd == -1)
{
if (errno != EEXIST)
g_error ("Unable to allocate shared mem for window");
return NULL;
}
*is_shm = TRUE;
res = ftruncate (fd, size);
g_assert (res != -1);
if (size == 0)
ptr = map;
else
{
HANDLE h, fm;
h = (HANDLE)_get_osfhandle (fd);
fm = CreateFileMapping (h, NULL, PAGE_READWRITE, 0, (DWORD)size, NULL);
ptr = MapViewOfFile (fm, FILE_MAP_WRITE, 0, 0, (size_t)size);
CloseHandle (fm);
}
(void) close(fd);
return ptr;
#else
#error "No shm mapping supported"
return NULL;
#endif
}
static char
make_valid_fs_char (char c)
{
char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890";
return chars[c % (sizeof (chars) - 1)];
}
/* name must have at least space for 34 bytes */
static gpointer
create_random_shm (char *name, gsize size, gboolean *is_shm)
{
guint32 r;
int i, o;
gpointer ptr;
while (TRUE)
{
o = 0;
name[o++] = '/';
name[o++] = 'b';
name[o++] = 'd';
name[o++] = 'w';
name[o++] = '-';
for (i = 0; i < 32/4 - 1; i++)
{
r = g_random_int ();
name[o++] = make_valid_fs_char ((r >> 0) & 0xff);
name[o++] = make_valid_fs_char ((r >> 8) & 0xff);
name[o++] = make_valid_fs_char ((r >> 16) & 0xff);
name[o++] = make_valid_fs_char ((r >> 24) & 0xff);
}
name[o++] = 0;
ptr = map_named_shm (name, size, is_shm);
if (ptr)
return ptr;
}
}
static const cairo_user_data_key_t gdk_broadway_shm_cairo_key;
typedef struct {
char name[36];
void *data;
gsize data_size;
gboolean is_shm;
} BroadwayShmSurfaceData;
static void
shm_data_destroy (void *_data)
{
BroadwayShmSurfaceData *data = _data;
#ifdef G_OS_UNIX
munmap (data->data, data->data_size);
if (data->is_shm)
shm_unlink (data->name);
else
{
char *filename = g_build_filename (g_get_tmp_dir (), data->name, NULL);
unlink (filename);
g_free (filename);
}
#elif defined(G_OS_WIN32)
char *name = data->name;
char *shmpath;
if (*name == '/')
++name;
shmpath = g_build_filename (g_get_tmp_dir (), name, NULL);
UnmapViewOfFile (data->data);
remove (shmpath);
g_free (shmpath);
#endif
g_free (data);
}
cairo_surface_t *
_gdk_broadway_server_create_surface (int width,
int height)
{
BroadwayShmSurfaceData *data;
cairo_surface_t *surface;
data = g_new (BroadwayShmSurfaceData, 1);
data->data_size = width * height * sizeof (guint32);
data->data = create_random_shm (data->name, data->data_size, &data->is_shm);
surface = cairo_image_surface_create_for_data ((guchar *)data->data,
CAIRO_FORMAT_ARGB32, width, height, width * sizeof (guint32));
g_assert (surface != NULL);
cairo_surface_set_user_data (surface, &gdk_broadway_shm_cairo_key,
data, shm_data_destroy);
return surface;
}
void
_gdk_broadway_server_window_update (GdkBroadwayServer *server,
gint id,
cairo_surface_t *surface)
guint32 texture)
{
BroadwayRequestUpdate msg;
BroadwayShmSurfaceData *data;
if (surface == NULL)
return;
data = cairo_surface_get_user_data (surface, &gdk_broadway_shm_cairo_key);
g_assert (data != NULL);
msg.id = id;
memcpy (msg.name, data->name, 36);
msg.width = cairo_image_surface_get_width (surface);
msg.height = cairo_image_surface_get_height (surface);
msg.texture = texture;
gdk_broadway_server_send_message (server, msg,
BROADWAY_REQUEST_UPDATE);

View File

@ -63,11 +63,9 @@ guint32 gdk_broadway_server_upload_texture (GdkBroadwaySer
GdkTexture *texture);
void gdk_broadway_server_release_texture (GdkBroadwayServer *server,
guint32 id);
cairo_surface_t *_gdk_broadway_server_create_surface (int width,
int height);
void _gdk_broadway_server_window_update (GdkBroadwayServer *server,
gint id,
cairo_surface_t *surface);
guint32 texture);
gboolean _gdk_broadway_server_window_move_resize (GdkBroadwayServer *server,
gint id,
gboolean with_move,

View File

@ -30,6 +30,7 @@
#include "gdkinternals.h"
#include "gdkdeviceprivate.h"
#include "gdkdevicemanager-broadway.h"
#include <gdk/gdktextureprivate.h>
#include <glib.h>
#include <glib/gprintf.h>

View File

@ -36,6 +36,7 @@
#include "gdkinternals.h"
#include "gdkdeviceprivate.h"
#include "gdkeventsource.h"
#include <gdk/gdktextureprivate.h>
#include <stdlib.h>
#include <stdio.h>
@ -112,11 +113,26 @@ update_dirty_windows_and_sync (void)
if (impl->dirty)
{
GdkTexture *texture;
guint32 texture_id;
impl->dirty = FALSE;
updated_surface = TRUE;
if (impl->texture_id)
gdk_broadway_server_release_texture (display->server, impl->texture_id);
impl->texture_id = 0;
texture = gdk_texture_new_for_surface (impl->surface);
texture_id = gdk_broadway_server_upload_texture (display->server, texture);
g_object_unref (texture);
impl->texture_id = texture_id;
_gdk_broadway_server_window_update (display->server,
impl->id,
impl->surface);
texture_id);
}
}
@ -246,7 +262,8 @@ _gdk_broadway_window_resize_surface (GdkWindow *window)
{
cairo_surface_destroy (impl->surface);
impl->surface = _gdk_broadway_server_create_surface (gdk_window_get_width (impl->wrapper),
impl->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
gdk_window_get_width (impl->wrapper),
gdk_window_get_height (impl->wrapper));
}
@ -283,7 +300,7 @@ gdk_window_broadway_ref_cairo_surface (GdkWindow *window)
/* Create actual backing store if missing */
if (!impl->surface)
impl->surface = _gdk_broadway_server_create_surface (w, h);
impl->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
/* Create a destroyable surface referencing the real one */
if (!impl->ref_surface)
@ -334,6 +351,9 @@ _gdk_broadway_window_destroy (GdkWindow *window,
g_hash_table_remove (broadway_display->id_ht, GINT_TO_POINTER (impl->id));
_gdk_broadway_server_destroy_window (broadway_display->server, impl->id);
if (impl->texture_id)
gdk_broadway_server_release_texture (broadway_display->server, impl->texture_id);
}
/* This function is called when the XWindow is really gone.

View File

@ -55,6 +55,7 @@ struct _GdkWindowImplBroadway
GdkCursor *cursor;
int id;
int texture_id;
gboolean visible;
gboolean maximized;

View File

@ -1,5 +1,4 @@
gdk_broadway_sources = files([
'broadway-buffer.c',
'broadway-output.c',
'broadway-server.c',
'broadwayd.c',
@ -60,7 +59,7 @@ broadwayjs_h = custom_target('broadwayjs.h',
executable('gtk4-broadwayd',
clienthtml_h, broadwayjs_h,
'broadwayd.c', 'broadway-server.c', 'broadway-buffer.c', 'broadway-output.c',
'broadwayd.c', 'broadway-server.c', 'broadway-output.c',
include_directories: [confinc, gdkinc],
c_args: ['-DGDK_COMPILATION', '-DG_LOG_DOMAIN="Gdk"', ],
dependencies : [broadwayd_syslib, gdk_deps],