Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Gnome/gdk/broadway/   (Gnome Linux Desktop Version 4.23.2©)  Datei vom 30.5.2026 mit Größe 16 kB image not shown  

Quelle  broadway-output.c

  Sprache: C
 

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <cairo.h>

#include "broadway-output.h"

//#define DEBUG_NODE_SENDING
//#define DEBUG_NODE_SENDING_REMOVE

/************************************************************************
 *                Basic I/O primitives                                  *
 ************************************************************************/


struct BroadwayOutput {
  GOutputStream *out;
  GString *buf;
  int error;
  guint32 serial;
};

static void
broadway_output_send_cmd (BroadwayOutput *output,
                          gboolean fin, BroadwayWSOpCode code,
                          const void *buf, gsize count)
{
  gboolean mask = FALSE;
  guchar header[16];
  size_t p;

  gboolean mid_header = count > 125 && count <= 65535;
  gboolean long_header = count > 65535;

  /* NB. big-endian spec => bit 0 == MSB */
  header[0] = ( (fin ? 0x80 : 0) | (code & 0x0f) );
  header[1] = ( (mask ? 0x80 : 0) |
                (mid_header ? 126 : long_header ? 127 : count) );
  p = 2;
  if (mid_header)
    {
      *(guint16 *)(header + p) = GUINT16_TO_BE( (guint16)count );
      p += 2;
    }
  else if (long_header)
    {
      *(guint64 *)(header + p) = GUINT64_TO_BE( count );
      p += 8;
    }
  // FIXME: if we are paranoid we should 'mask' the data
  // FIXME: we should really emit these as a single write
  g_output_stream_write_all (output->out, header, p, NULL, NULL, NULL);
  g_output_stream_write_all (output->out, buf, count, NULL, NULL, NULL);
}

void broadway_output_pong (BroadwayOutput *output)
{
  broadway_output_send_cmd (output, TRUE, BROADWAY_WS_CNX_PONG, NULL, 0);
}

int
broadway_output_flush (BroadwayOutput *output)
{
  if (output->buf->len == 0)
    return TRUE;

  broadway_output_send_cmd (output, TRUE, BROADWAY_WS_BINARY,
                            output->buf->str, output->buf->len);

  g_string_set_size (output->buf, 0);

  return !output->error;

}

BroadwayOutput *
broadway_output_new (GOutputStream *out, guint32 serial)
{
  BroadwayOutput *output;

  output = g_new0 (BroadwayOutput, 1);

  output->out = g_object_ref (out);
  output->buf = g_string_new ("");
  output->serial = serial;

  return output;
}

void
broadway_output_free (BroadwayOutput *output)
{
  g_object_unref (output->out);
  free (output);
}

guint32
broadway_output_get_next_serial (BroadwayOutput *output)
{
  return output->serial;
}

void
broadway_output_set_next_serial (BroadwayOutput *output,
                                 guint32 serial)
{
  output->serial = serial;
}


/************************************************************************
 *                     Core rendering operations                        *
 ************************************************************************/


static void
append_uint8 (BroadwayOutput *output, guint8 c)
{
  g_string_append_c (output->buf, c);
}

static void
append_bool (BroadwayOutput *output, gboolean val)
{
  g_string_append_c (output->buf, val ? 10);
}

static void
append_flags (BroadwayOutput *output, guint32 val)
{
  g_string_append_c (output->buf, val);
}

static void
append_uint16 (BroadwayOutput *output, guint32 v)
{
  gsize old_len = output->buf->len;
  guint8 *buf;

  g_string_set_size (output->buf, old_len + 2);
  buf = (guint8 *)output->buf->str + old_len;
  buf[0] = (v >> 0) & 0xff;
  buf[1] = (v >> 8) & 0xff;
}

static void
append_uint32 (BroadwayOutput *output, guint32 v)
{
  gsize old_len = output->buf->len;
  guint8 *buf;

  g_string_set_size (output->buf, old_len + 4);
  buf = (guint8 *)output->buf->str + old_len;
  buf[0] = (v >> 0) & 0xff;
  buf[1] = (v >> 8) & 0xff;
  buf[2] = (v >> 16) & 0xff;
  buf[3] = (v >> 24) & 0xff;
}

static void
patch_uint32 (BroadwayOutput *output, guint32 v, gsize offset)
{
  guint8 *buf;

  buf = (guint8 *)output->buf->str + offset;
  buf[0] = (v >> 0) & 0xff;
  buf[1] = (v >> 8) & 0xff;
  buf[2] = (v >> 16) & 0xff;
  buf[3] = (v >> 24) & 0xff;
}


static void
write_header(BroadwayOutput *output, char op)
{
  append_uint8 (output, op);
  append_uint32 (output, output->serial++);
}

void
broadway_output_grab_pointer (BroadwayOutput *output,
                              int id,
                              gboolean owner_event)
{
  write_header (output, BROADWAY_OP_GRAB_POINTER);
  append_uint16 (output, id);
  append_bool (output, owner_event);
}

guint32
broadway_output_ungrab_pointer (BroadwayOutput *output)
{
  guint32 serial;

  serial = output->serial;
  write_header (output, BROADWAY_OP_UNGRAB_POINTER);

  return serial;
}

void
broadway_output_new_surface(BroadwayOutput *output,
                            int id, int x, int y, int w, int h)
{
  write_header (output, BROADWAY_OP_NEW_SURFACE);
  append_uint16 (output, id);
  append_uint16 (output, x);
  append_uint16 (output, y);
  append_uint16 (output, w);
  append_uint16 (output, h);
}

void
broadway_output_disconnected (BroadwayOutput *output)
{
  write_header (output, BROADWAY_OP_DISCONNECTED);
}

void
broadway_output_show_surface(BroadwayOutput *output,  int id)
{
  write_header (output, BROADWAY_OP_SHOW_SURFACE);
  append_uint16 (output, id);
}

void
broadway_output_hide_surface(BroadwayOutput *output,  int id)
{
  write_header (output, BROADWAY_OP_HIDE_SURFACE);
  append_uint16 (output, id);
}

void
broadway_output_raise_surface(BroadwayOutput *output,  int id)
{
  write_header (output, BROADWAY_OP_RAISE_SURFACE);
  append_uint16 (output, id);
}

void
broadway_output_lower_surface(BroadwayOutput *output,  int id)
{
  write_header (output, BROADWAY_OP_LOWER_SURFACE);
  append_uint16 (output, id);
}

void
broadway_output_destroy_surface(BroadwayOutput *output,  int id)
{
  write_header (output, BROADWAY_OP_DESTROY_SURFACE);
  append_uint16 (output, id);
}

void
broadway_output_roundtrip (BroadwayOutput *output,
                           int             id,
                           guint32         tag)
{
  write_header (output, BROADWAY_OP_ROUNDTRIP);
  append_uint16 (output, id);
  append_uint32 (output, tag);
}

void
broadway_output_set_show_keyboard (BroadwayOutput *output,
                                   gboolean show)
{
  write_header (output, BROADWAY_OP_SET_SHOW_KEYBOARD);
  append_uint16 (output, show);
}

void
broadway_output_move_resize_surface (BroadwayOutput *output,
                                     int             id,
                                     gboolean        has_pos,
                                     int             x,
                                     int             y,
                                     gboolean        has_size,
                                     int             w,
                                     int             h)
{
  int val;

  if (!has_pos && !has_size)
    return;

  write_header (output, BROADWAY_OP_MOVE_RESIZE);
  val = (!!has_pos) | ((!!has_size) << 1);
  append_uint16 (output, id);
  append_flags (output, val);
  if (has_pos)
    {
      append_uint16 (output, x);
      append_uint16 (output, y);
    }
  if (has_size)
    {
      append_uint16 (output, w);
      append_uint16 (output, h);
    }
}

void
broadway_output_set_transient_for (BroadwayOutput *output,
                                   int             id,
                                   int             parent_id)
{
  write_header (output, BROADWAY_OP_SET_TRANSIENT_FOR);
  append_uint16 (output, id);
  append_uint16 (output, parent_id);
}

static int append_node_depth = -1;

static void
append_type (BroadwayOutput *output, guint32 type, BroadwayNode *node)
{
#ifdef DEBUG_NODE_SENDING
  g_print ("%*s%s(%d/%d)", append_node_depth*2"", broadway_node_type_names[type], node->id, node->output_id);
  if (type == BROADWAY_NODE_TEXTURE)
    g_print (" tx=%u", node->data[4]);
  g_print ("\n");
#endif

  append_uint32 (output, type);
}

static BroadwayNode *
lookup_old_node (GHashTable *old_node_lookup,
                 guint32 id)
{
  if (old_node_lookup)
    return g_hash_table_lookup (old_node_lookup, GINT_TO_POINTER (id));

  return NULL;
}


/***********************************
 * This outputs the tree to the client, while at the same time diffing
 * against the old tree.  This allows us to avoid sending certain
 * parts.
 *
 * Reusing existing dom nodes are problematic because doing so
 * automatically inherits all their children.  There are two cases
 * where we do this:
 *
 * If the entire sub tree is identical we emit a KEEP_ALL node which
 * just reuses the entire old dom subtree.
 *
 * If a the node is unchanged (but some descendant may have changed),
 * and all parents are also unchanged, then we can just avoid
 * changing the dom node at all, and we emit a KEEP_THIS node.
 *
 ***********************************/

static void
append_node (BroadwayOutput *output,
             BroadwayNode   *node,
             GHashTable     *old_node_lookup)
{
  guint32 i;
  BroadwayNode *reused_node;

  append_node_depth++;

  reused_node = lookup_old_node (old_node_lookup, node->id);
  if (reused_node)
    {
      broadway_node_mark_deep_consumed (reused_node, TRUE);
      append_type (output, BROADWAY_NODE_REUSE, node);
      append_uint32 (output, node->output_id);
    }
  else
    {
      append_type (output, node->type, node);
      append_uint32 (output, node->output_id);
      for (i = 0; i < node->n_data; i++)
        append_uint32 (output, node->data[i]);
      for (i = 0; i < node->n_children; i++)
        append_node (output,
                     node->children[i],
                     old_node_lookup);
    }

  append_node_depth--;
}

static gboolean
should_reuse_node (BroadwayOutput *output,
                   BroadwayNode   *node,
                   BroadwayNode   *old_node)
{
  int i;
  guint32 new_texture;

  if (old_node->reused)
    return FALSE;

  if (node->type != old_node->type)
    return FALSE;

  if (broadway_node_equal (node, old_node))
    return TRUE;

  switch (node->type) {
  case BROADWAY_NODE_TRANSFORM:
#ifdef DEBUG_NODE_SENDING
   g_print ("Patching transform node %d/%d\n",
            old_node->id, old_node->output_id);
#endif
    append_uint32 (output, BROADWAY_NODE_OP_PATCH_TRANSFORM);
    append_uint32 (output, old_node->output_id);
    for (i = 0; i < node->n_data; i++)
      append_uint32 (output, node->data[i]);
    return TRUE;

  case BROADWAY_NODE_TEXTURE:
    /* Check that the size, etc is the same */
    for (i = 0; i < 4; i++)
      if (node->data[i] != old_node->data[i])
        return FALSE;

    new_texture = node->data[4];

#ifdef DEBUG_NODE_SENDING
   g_print ("Patching texture node %d/%d to tx=%d\n",
            old_node->id, old_node->output_id,
            new_texture);
#endif
    append_uint32 (output, BROADWAY_NODE_OP_PATCH_TEXTURE);
    append_uint32 (output, old_node->output_id);
    append_uint32 (output, new_texture);
    return TRUE;
    break;
  default:
    return FALSE;
  }
}


static BroadwayNode *
append_node_ops (BroadwayOutput *output,
                 BroadwayNode   *node,
                 BroadwayNode   *parent,
                 BroadwayNode   *previous_sibling,
                 BroadwayNode   *old_node,
                 GHashTable     *old_node_lookup)
{
  BroadwayNode *reused_node;
  guint32 i;

  /* Maybe can be reused from the last tree. */
  reused_node = lookup_old_node (old_node_lookup, node->id);
  if (reused_node)
    {
      g_assert (node == reused_node);
      g_assert (reused_node->reused);
      g_assert (!reused_node->consumed); /* Should only be once in the tree, and not consumed otherwise */

      broadway_node_mark_deep_consumed (reused_node, TRUE);

      if (node == old_node)
        {
          /* The node in the old tree at the current position is the same, so
             we need to do nothing, just don't delete it (which we won't since
             its marked used) */

        }
      else
        {
          /* We can reuse it, bu it comes from a different place or
             order, if so we need to move it in place */

#ifdef DEBUG_NODE_SENDING
          g_print ("Move old node %d/%d to parent %d/%d after %d/%d\n",
                   reused_node->id, reused_node->output_id,
                   parent ? parent->id : 0,
                   parent ? parent->output_id : 0,
                   previous_sibling ? previous_sibling->id : 0,
                   previous_sibling ? previous_sibling->output_id : 0);
#endif
          append_uint32 (output, BROADWAY_NODE_OP_MOVE_AFTER_CHILD);
          append_uint32 (output, parent ? parent->output_id : 0);
          append_uint32 (output, previous_sibling ? previous_sibling->output_id : 0);
          append_uint32 (output, reused_node->output_id);
        }

      return reused_node;
    }

  /* If the next node in place is shallowly equal (but not necessarily
   * deep equal) we reuse it and tweak its children as needed.
   * Except we avoid this for reused node as those make more sense to reuse deeply.
   */


  if (old_node && should_reuse_node (output, node, old_node))
    {
      int old_i = 0;
      BroadwayNode *last_child = NULL;

      old_node->consumed = TRUE// Don't reuse again

      // We rewrite this new node as it now represents the old node in the browser
      node->output_id = old_node->output_id;

      /* However, we might need to rewrite then children of old_node */
      for (i = 0; i < node->n_children; i++)
        {
          BroadwayNode *child = node->children[i];

          /* Find the next (or first) non-consumed old child, if any */
          while (old_i < old_node->n_children &&
                 old_node->children[old_i]->consumed)
            old_i++;

          last_child =
            append_node_ops (output,
                             child,
                             node, /* parent */
                             last_child,
                             (old_i < old_node->n_children) ? old_node->children[old_i] : NULL,
                             old_node_lookup);
        }

      /* Remaining old nodes are either reused elsewhere, or end up marked not consumed so are deleted at the end */
      return old_node;
    }

  /* Fallback to create a new tree */
#ifdef DEBUG_NODE_SENDING
   g_print ("Insert nodes in parent %d/%d, after sibling %d/%d\n",
            parent ? parent->id : 0,
            parent ? parent->output_id : 0,
            previous_sibling ? previous_sibling->id : 0,
            previous_sibling ? previous_sibling->output_id : 0);
#endif
   append_uint32 (output, BROADWAY_NODE_OP_INSERT_NODE);
   append_uint32 (output, parent ? parent->output_id : 0);
   append_uint32 (output, previous_sibling ? previous_sibling->output_id : 0);

   append_node(output, node, old_node_lookup);

   return node;
}

/* Remove non-consumed nodes */
static void
append_node_removes (BroadwayOutput *output,
                     BroadwayNode   *node)
{
  // TODO: Use an array of nodes instead
  if (!node->consumed)
    {
#ifdef DEBUG_NODE_SENDING_REMOVE
          g_print ("Remove old node non-consumed node %d/%d\n",
                   node->id, node->output_id);
#endif
      append_uint32 (output, BROADWAY_NODE_OP_REMOVE_NODE);
      append_uint32 (output, node->output_id);
    }

  for (int i = 0; i < node->n_children; i++)
    append_node_removes (output, node->children[i]);
}



void
broadway_output_surface_set_nodes (BroadwayOutput *output,
                                   int             id,
                                   BroadwayNode   *root,
                                   BroadwayNode   *old_root,
                                   GHashTable     *old_node_lookup)
{
  gsize size_pos, start, end;


  if (old_root)
    {
      broadway_node_mark_deep_consumed (old_root, FALSE);
      broadway_node_mark_deep_reused (old_root, FALSE);
      /* This will modify children of old_root if any are shared */
      broadway_node_mark_deep_reused (root, TRUE);
    }

  write_header (output, BROADWAY_OP_SET_NODES);

  append_uint16 (output, id);

  size_pos = output->buf->len;
  append_uint32 (output, 0);

  start = output->buf->len;
#ifdef DEBUG_NODE_SENDING
  g_print ("====== node ops for surface %d =======\n", id);
#endif
  append_node_ops (output, root, NULL, NULL, old_root, old_node_lookup);
  if (old_root)
    append_node_removes (output, old_root);
  end = output->buf->len;
  patch_uint32 (output, (end - start) / 4, size_pos);
}

void
broadway_output_upload_texture (BroadwayOutput *output,
                                guint32 id,
                                GBytes *texture)
{
  gsize len = g_bytes_get_size (texture);
  write_header (output, BROADWAY_OP_UPLOAD_TEXTURE);
  append_uint32 (output, id);
  append_uint32 (output, (guint32)len);
  g_string_append_len (output->buf, g_bytes_get_data (texture, NULL), len);
}

void
broadway_output_release_texture (BroadwayOutput *output,
                                 guint32 id)
{
  write_header (output, BROADWAY_OP_RELEASE_TEXTURE);
  append_uint32 (output, id);
}

Messung V0.5 in Prozent
C=98 H=96 G=96

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet am  2026-07-02) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.