summaryrefslogtreecommitdiffstats
path: root/libxrdp/xrdp_orders.c
diff options
context:
space:
mode:
Diffstat (limited to 'libxrdp/xrdp_orders.c')
-rw-r--r--libxrdp/xrdp_orders.c1553
1 files changed, 1553 insertions, 0 deletions
diff --git a/libxrdp/xrdp_orders.c b/libxrdp/xrdp_orders.c
new file mode 100644
index 00000000..4ee7cae0
--- /dev/null
+++ b/libxrdp/xrdp_orders.c
@@ -0,0 +1,1553 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ xrdp: A Remote Desktop Protocol server.
+ Copyright (C) Jay Sorg 2004-2005
+
+ orders
+
+*/
+
+#include "libxrdp.h"
+
+/*****************************************************************************/
+struct xrdp_orders* APP_CC
+xrdp_orders_create(struct xrdp_session* session, struct xrdp_rdp* rdp_layer)
+{
+ struct xrdp_orders* self;
+
+ self = (struct xrdp_orders*)g_malloc(sizeof(struct xrdp_orders), 1);
+ self->session = session;
+ self->rdp_layer = rdp_layer;
+ make_stream(self->out_s);
+ init_stream(self->out_s, 8192);
+ return self;
+}
+
+/*****************************************************************************/
+void APP_CC
+xrdp_orders_delete(struct xrdp_orders* self)
+{
+ if (self == 0)
+ {
+ return;
+ }
+ free_stream(self->out_s);
+ g_free(self);
+}
+
+/*****************************************************************************/
+/* returns error */
+int APP_CC
+xrdp_orders_init(struct xrdp_orders* self)
+{
+ self->order_level++;
+ if (self->order_level == 1)
+ {
+ self->order_count = 0;
+ /* is this big enough */
+ if (xrdp_rdp_init_data(self->rdp_layer, self->out_s) != 0)
+ {
+ return 1;
+ }
+ out_uint16_le(self->out_s, RDP_UPDATE_ORDERS);
+ out_uint8s(self->out_s, 2); /* pad */
+ self->order_count_ptr = self->out_s->p;
+ out_uint8s(self->out_s, 2); /* number of orders, set later */
+ out_uint8s(self->out_s, 2); /* pad */
+ }
+ return 0;
+}
+
+/*****************************************************************************/
+/* returns error */
+int APP_CC
+xrdp_orders_send(struct xrdp_orders* self)
+{
+ int rv;
+
+ rv = 0;
+ if (self->order_level > 0)
+ {
+ self->order_level--;
+ if (self->order_level == 0 && self->order_count > 0)
+ {
+ s_mark_end(self->out_s);
+ DEBUG(("xrdp_orders_send sending %d orders\n\r", self->order_count));
+ self->order_count_ptr[0] = self->order_count;
+ self->order_count_ptr[1] = self->order_count >> 8;
+ if (xrdp_rdp_send_data(self->rdp_layer, self->out_s,
+ RDP_DATA_PDU_UPDATE) != 0)
+ {
+ rv = 1;
+ }
+ }
+ }
+ return rv;
+}
+
+/*****************************************************************************/
+/* returns error */
+int APP_CC
+xrdp_orders_force_send(struct xrdp_orders* self)
+{
+ if (self->order_count > 0)
+ {
+ s_mark_end(self->out_s);
+ DEBUG(("xrdp_orders_force_send sending %d orders\n\r", self->order_count));
+ self->order_count_ptr[0] = self->order_count;
+ self->order_count_ptr[1] = self->order_count >> 8;
+ if (xrdp_rdp_send_data(self->rdp_layer, self->out_s,
+ RDP_DATA_PDU_UPDATE) != 0)
+ {
+ return 1;
+ }
+ }
+ self->order_count = 0;
+ self->order_level = 0;
+ return 0;
+}
+
+/*****************************************************************************/
+/* check if the current order will fix in packet size of 8192, if not */
+/* send what we got and init a new one */
+/* returns error */
+static int APP_CC
+xrdp_orders_check(struct xrdp_orders* self, int max_size)
+{
+ int size;
+
+ if (self->order_level < 1)
+ {
+ if (max_size > 8000)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ size = self->out_s->p - self->order_count_ptr;
+ if (size < 0 || size > 8192)
+ {
+ return 1;
+ }
+ if (size + max_size + 100 > 8000)
+ {
+ xrdp_orders_force_send(self);
+ xrdp_orders_init(self);
+ }
+ return 0;
+}
+
+/*****************************************************************************/
+/* check if rect is the same as the last one sent */
+/* returns boolean */
+static int APP_CC
+xrdp_orders_last_bounds(struct xrdp_orders* self, struct xrdp_rect* rect)
+{
+ if (rect == 0)
+ {
+ return 0;
+ }
+ if (rect->left == self->clip_left &&
+ rect->top == self->clip_top &&
+ rect->right == self->clip_right &&
+ rect->bottom == self->clip_bottom)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+/*****************************************************************************/
+/* check if all coords are withing 256 bytes */
+/* returns boolean */
+static int APP_CC
+xrdp_orders_send_delta(struct xrdp_orders* self, int* vals, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i += 2)
+ {
+ if (g_abs(vals[i] - vals[i + 1]) >= 128)
+ {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*****************************************************************************/
+/* returns error */
+static int APP_CC
+xrdp_orders_out_bounds(struct xrdp_orders* self, struct xrdp_rect* rect)
+{
+ char* bounds_flags_ptr;
+ int bounds_flags;
+
+ bounds_flags = 0;
+ bounds_flags_ptr = self->out_s->p;
+ out_uint8s(self->out_s, 1);
+ /* left */
+ if (rect->left == self->clip_left)
+ {
+ }
+ else if (g_abs(rect->left - self->clip_left) < 128)
+ {
+ bounds_flags |= 0x10;
+ }
+ else
+ {
+ bounds_flags |= 0x01;
+ }
+ /* top */
+ if (rect->top == self->clip_top)
+ {
+ }
+ else if (g_abs(rect->top - self->clip_top) < 128)
+ {
+ bounds_flags |= 0x20;
+ }
+ else
+ {
+ bounds_flags |= 0x02;
+ }
+ /* right */
+ if (rect->right == self->clip_right)
+ {
+ }
+ else if (g_abs(rect->right - self->clip_right) < 128)
+ {
+ bounds_flags |= 0x40;
+ }
+ else
+ {
+ bounds_flags |= 0x04;
+ }
+ /* bottom */
+ if (rect->bottom == self->clip_bottom)
+ {
+ }
+ else if (g_abs(rect->bottom - self->clip_bottom) < 128)
+ {
+ bounds_flags |= 0x80;
+ }
+ else
+ {
+ bounds_flags |= 0x08;
+ }
+ /* left */
+ if (bounds_flags & 0x01)
+ {
+ out_uint16_le(self->out_s, rect->left);
+ }
+ else if (bounds_flags & 0x10)
+ {
+ out_uint8(self->out_s, rect->left - self->clip_left);
+ }
+ self->clip_left = rect->left;
+ /* top */
+ if (bounds_flags & 0x02)
+ {
+ out_uint16_le(self->out_s, rect->top);
+ }
+ else if (bounds_flags & 0x20)
+ {
+ out_uint8(self->out_s, rect->top - self->clip_top);
+ }
+ self->clip_top = rect->top;
+ /* right */
+ if (bounds_flags & 0x04)
+ {
+ out_uint16_le(self->out_s, rect->right);
+ }
+ else if (bounds_flags & 0x40)
+ {
+ out_uint8(self->out_s, rect->right - self->clip_right);
+ }
+ self->clip_right = rect->right;
+ /* bottom */
+ if (bounds_flags & 0x08)
+ {
+ out_uint16_le(self->out_s, rect->bottom);
+ }
+ else if (bounds_flags & 0x80)
+ {
+ out_uint8(self->out_s, rect->bottom - self->clip_bottom);
+ }
+ self->clip_bottom = rect->bottom;
+ /* set flags */
+ *bounds_flags_ptr = bounds_flags;
+ return 0;
+}
+
+/*****************************************************************************/
+/* returns error */
+/* send a solid rect to client */
+/* max size 23 */
+int APP_CC
+xrdp_orders_rect(struct xrdp_orders* self, int x, int y, int cx, int cy,
+ int color, struct xrdp_rect* rect)
+{
+ int order_flags;
+ int vals[8];
+ int present;
+ char* present_ptr;
+
+ xrdp_orders_check(self, 23);
+ self->order_count++;
+ order_flags = RDP_ORDER_STANDARD;
+ if (self->last_order != RDP_ORDER_RECT)
+ {
+ order_flags |= RDP_ORDER_CHANGE;
+ }
+ self->last_order = RDP_ORDER_RECT;
+ if (rect != 0)
+ {
+ order_flags |= RDP_ORDER_BOUNDS;
+ if (xrdp_orders_last_bounds(self, rect))
+ {
+ order_flags |= RDP_ORDER_LASTBOUNDS;
+ }
+ }
+ vals[0] = x;
+ vals[1] = self->rect_x;
+ vals[2] = y;
+ vals[3] = self->rect_y;
+ vals[4] = cx;
+ vals[5] = self->rect_cx;
+ vals[6] = cy;
+ vals[7] = self->rect_cy;
+ if (xrdp_orders_send_delta(self, vals, 8))
+ {
+ order_flags |= RDP_ORDER_DELTA;
+ }
+ out_uint8(self->out_s, order_flags)
+ if (order_flags & RDP_ORDER_CHANGE)
+ {
+ out_uint8(self->out_s, self->last_order);
+ }
+ present = 0;
+ present_ptr = self->out_s->p; /* hold 1 byte present pointer */
+ out_uint8s(self->out_s, 1)
+ if ((order_flags & RDP_ORDER_BOUNDS) &&
+ !(order_flags & RDP_ORDER_LASTBOUNDS))
+ {
+ xrdp_orders_out_bounds(self, rect);
+ }
+ if (x != self->rect_x)
+ {
+ present |= 0x01;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, x - self->rect_x);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, x);
+ }
+ self->rect_x = x;
+ }
+ if (y != self->rect_y)
+ {
+ present |= 0x02;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, y - self->rect_y);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, y);
+ }
+ self->rect_y = y;
+ }
+ if (cx != self->rect_cx)
+ {
+ present |= 0x04;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, cx - self->rect_cx);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, cx);
+ }
+ self->rect_cx = cx;
+ }
+ if (cy != self->rect_cy)
+ {
+ present |= 0x08;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, cy - self->rect_cy);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, cy);
+ }
+ self->rect_cy = cy;
+ }
+ if ((color & 0xff) != (self->rect_color & 0xff))
+ {
+ present |= 0x10;
+ self->rect_color = (self->rect_color & 0xffff00) | (color & 0xff);
+ out_uint8(self->out_s, color);
+ }
+ if ((color & 0xff00) != (self->rect_color & 0xff00))
+ {
+ present |= 0x20;
+ self->rect_color = (self->rect_color & 0xff00ff) | (color & 0xff00);
+ out_uint8(self->out_s, color >> 8);
+ }
+ if ((color & 0xff0000) != (self->rect_color & 0xff0000))
+ {
+ present |= 0x40;
+ self->rect_color = (self->rect_color & 0x00ffff) | (color & 0xff0000);
+ out_uint8(self->out_s, color >> 16);
+ }
+ present_ptr[0] = present;
+ return 0;
+}
+
+/*****************************************************************************/
+/* returns error */
+/* send a screen blt order */
+/* max size 25 */
+int APP_CC
+xrdp_orders_screen_blt(struct xrdp_orders* self, int x, int y,
+ int cx, int cy, int srcx, int srcy,
+ int rop, struct xrdp_rect* rect)
+{
+ int order_flags;
+ int vals[12];
+ int present;
+ char* present_ptr;
+
+ xrdp_orders_check(self, 25);
+ self->order_count++;
+ order_flags = RDP_ORDER_STANDARD;
+ if (self->last_order != RDP_ORDER_SCREENBLT)
+ {
+ order_flags |= RDP_ORDER_CHANGE;
+ }
+ self->last_order = RDP_ORDER_SCREENBLT;
+ if (rect != 0)
+ {
+ order_flags |= RDP_ORDER_BOUNDS;
+ if (xrdp_orders_last_bounds(self, rect))
+ {
+ order_flags |= RDP_ORDER_LASTBOUNDS;
+ }
+ }
+ vals[0] = x;
+ vals[1] = self->scr_blt_x;
+ vals[2] = y;
+ vals[3] = self->scr_blt_y;
+ vals[4] = cx;
+ vals[5] = self->scr_blt_cx;
+ vals[6] = cy;
+ vals[7] = self->scr_blt_cy;
+ vals[8] = srcx;
+ vals[9] = self->scr_blt_srcx;
+ vals[10] = srcy;
+ vals[11] = self->scr_blt_srcy;
+ if (xrdp_orders_send_delta(self, vals, 12))
+ {
+ order_flags |= RDP_ORDER_DELTA;
+ }
+ out_uint8(self->out_s, order_flags);
+ if (order_flags & RDP_ORDER_CHANGE)
+ {
+ out_uint8(self->out_s, self->last_order);
+ }
+ present = 0;
+ present_ptr = self->out_s->p; /* hold 1 byte present pointer */
+ out_uint8s(self->out_s, 1)
+ if ((order_flags & RDP_ORDER_BOUNDS) &&
+ !(order_flags & RDP_ORDER_LASTBOUNDS))
+ {
+ xrdp_orders_out_bounds(self, rect);
+ }
+ if (x != self->scr_blt_x)
+ {
+ present |= 0x01;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, x - self->scr_blt_x);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, x);
+ }
+ self->scr_blt_x = x;
+ }
+ if (y != self->scr_blt_y)
+ {
+ present |= 0x02;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, y - self->scr_blt_y);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, y);
+ }
+ self->scr_blt_y = y;
+ }
+ if (cx != self->scr_blt_cx)
+ {
+ present |= 0x04;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, cx - self->scr_blt_cx);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, cx);
+ }
+ self->scr_blt_cx = cx;
+ }
+ if (cy != self->scr_blt_cy)
+ {
+ present |= 0x08;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, cy - self->scr_blt_cy);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, cy);
+ }
+ self->scr_blt_cy = cy;
+ }
+ if (rop != self->scr_blt_rop)
+ {
+ present |= 0x10;
+ out_uint8(self->out_s, rop);
+ self->scr_blt_rop = rop;
+ }
+ if (srcx != self->scr_blt_srcx)
+ {
+ present |= 0x20;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, srcx - self->scr_blt_srcx);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, srcx);
+ }
+ self->scr_blt_srcx = srcx;
+ }
+ if (srcy != self->scr_blt_srcy)
+ {
+ present |= 0x40;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, srcy - self->scr_blt_srcy)
+ }
+ else
+ {
+ out_uint16_le(self->out_s, srcy)
+ }
+ self->scr_blt_srcy = srcy;
+ }
+ present_ptr[0] = present;
+ return 0;
+}
+
+/*****************************************************************************/
+/* returns error */
+/* send a pat blt order */
+/* max size 39 */
+int APP_CC
+xrdp_orders_pat_blt(struct xrdp_orders* self, int x, int y,
+ int cx, int cy, int rop, int bg_color,
+ int fg_color, struct xrdp_brush* brush,
+ struct xrdp_rect* rect)
+{
+ int order_flags;
+ int vals[8];
+ int present;
+ char* present_ptr;
+ struct xrdp_brush blank_brush;
+
+ xrdp_orders_check(self, 39);
+ self->order_count++;
+ order_flags = RDP_ORDER_STANDARD;
+ if (self->last_order != RDP_ORDER_PATBLT)
+ {
+ order_flags |= RDP_ORDER_CHANGE;
+ }
+ self->last_order = RDP_ORDER_PATBLT;
+ if (rect != 0)
+ {
+ order_flags |= RDP_ORDER_BOUNDS;
+ if (xrdp_orders_last_bounds(self, rect))
+ {
+ order_flags |= RDP_ORDER_LASTBOUNDS;
+ }
+ }
+ vals[0] = x;
+ vals[1] = self->pat_blt_x;
+ vals[2] = y;
+ vals[3] = self->pat_blt_y;
+ vals[4] = cx;
+ vals[5] = self->pat_blt_cx;
+ vals[6] = cy;
+ vals[7] = self->pat_blt_cy;
+ if (xrdp_orders_send_delta(self, vals, 8))
+ {
+ order_flags |= RDP_ORDER_DELTA;
+ }
+ out_uint8(self->out_s, order_flags);
+ if (order_flags & RDP_ORDER_CHANGE)
+ {
+ out_uint8(self->out_s, self->last_order);
+ }
+ present = 0;
+ present_ptr = self->out_s->p; /* hold 2 byte present pointer, todo */
+ out_uint8s(self->out_s, 2) /* this can be smaller, */
+ /* see RDP_ORDER_SMALL and RDP_ORDER_TINY */
+ if ((order_flags & RDP_ORDER_BOUNDS) &&
+ !(order_flags & RDP_ORDER_LASTBOUNDS))
+ {
+ xrdp_orders_out_bounds(self, rect);
+ }
+ if (x != self->pat_blt_x)
+ {
+ present |= 0x0001;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, x - self->pat_blt_x);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, x);
+ }
+ self->pat_blt_x = x;
+ }
+ if (y != self->pat_blt_y)
+ {
+ present |= 0x0002;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, y - self->pat_blt_y);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, y);
+ }
+ self->pat_blt_y = y;
+ }
+ if (cx != self->pat_blt_cx)
+ {
+ present |= 0x0004;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, cx - self->pat_blt_cx);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, cx);
+ }
+ self->pat_blt_cx = cx;
+ }
+ if (cy != self->pat_blt_cy)
+ {
+ present |= 0x0008;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, cy - self->pat_blt_cy);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, cy);
+ }
+ self->pat_blt_cy = cy;
+ }
+ if (rop != self->pat_blt_rop)
+ {
+ present |= 0x0010;
+ /* PATCOPY PATPAINT PATINVERT DSTINVERT BLACKNESS WHITENESS */
+ out_uint8(self->out_s, rop);
+ self->pat_blt_rop = rop;
+ }
+ if (bg_color != self->pat_blt_bg_color)
+ {
+ present |= 0x0020;
+ out_uint8(self->out_s, bg_color);
+ out_uint8(self->out_s, bg_color >> 8);
+ out_uint8(self->out_s, bg_color >> 16);
+ self->pat_blt_bg_color = bg_color;
+ }
+ if (fg_color != self->pat_blt_fg_color)
+ {
+ present |= 0x0040;
+ out_uint8(self->out_s, fg_color);
+ out_uint8(self->out_s, fg_color >> 8);
+ out_uint8(self->out_s, fg_color >> 16);
+ self->pat_blt_fg_color = fg_color;
+ }
+ if (brush == 0) /* if nil use blank one */
+ { /* todo can we just set style to zero */
+ g_memset(&blank_brush, 0, sizeof(struct xrdp_brush));
+ brush = &blank_brush;
+ }
+ if (brush->x_orgin != self->pat_blt_brush.x_orgin)
+ {
+ present |= 0x0080;
+ out_uint8(self->out_s, brush->x_orgin);
+ self->pat_blt_brush.x_orgin = brush->x_orgin;
+ }
+ if (brush->y_orgin != self->pat_blt_brush.y_orgin)
+ {
+ present |= 0x0100;
+ out_uint8(self->out_s, brush->y_orgin);
+ self->pat_blt_brush.y_orgin = brush->y_orgin;
+ }
+ if (brush->style != self->pat_blt_brush.style)
+ {
+ present |= 0x0200;
+ out_uint8(self->out_s, brush->style);
+ self->pat_blt_brush.style = brush->style;
+ }
+ if (brush->pattern[0] != self->pat_blt_brush.pattern[0])
+ {
+ present |= 0x0400;
+ out_uint8(self->out_s, brush->pattern[0]);
+ self->pat_blt_brush.pattern[0] = brush->pattern[0];
+ }
+ if (g_memcmp(brush->pattern + 1, self->pat_blt_brush.pattern + 1, 7) != 0)
+ {
+ present |= 0x0800;
+ out_uint8a(self->out_s, brush->pattern + 1, 7);
+ g_memcpy(self->pat_blt_brush.pattern + 1, brush->pattern + 1, 7);
+ }
+ present_ptr[0] = present;
+ present_ptr[1] = present >> 8;
+ return 0;
+}
+
+/*****************************************************************************/
+/* returns error */
+/* send a dest blt order */
+/* max size 21 */
+int APP_CC
+xrdp_orders_dest_blt(struct xrdp_orders* self, int x, int y,
+ int cx, int cy, int rop,
+ struct xrdp_rect* rect)
+{
+ int order_flags;
+ int vals[8];
+ int present;
+ char* present_ptr;
+
+ xrdp_orders_check(self, 21);
+ self->order_count++;
+ order_flags = RDP_ORDER_STANDARD;
+ if (self->last_order != RDP_ORDER_DESTBLT)
+ {
+ order_flags |= RDP_ORDER_CHANGE;
+ }
+ self->last_order = RDP_ORDER_DESTBLT;
+ if (rect != 0)
+ {
+ order_flags |= RDP_ORDER_BOUNDS;
+ if (xrdp_orders_last_bounds(self, rect))
+ {
+ order_flags |= RDP_ORDER_LASTBOUNDS;
+ }
+ }
+ vals[0] = x;
+ vals[1] = self->dest_blt_x;
+ vals[2] = y;
+ vals[3] = self->dest_blt_y;
+ vals[4] = cx;
+ vals[5] = self->dest_blt_cx;
+ vals[6] = cy;
+ vals[7] = self->dest_blt_cy;
+ if (xrdp_orders_send_delta(self, vals, 8))
+ {
+ order_flags |= RDP_ORDER_DELTA;
+ }
+ out_uint8(self->out_s, order_flags);
+ if (order_flags & RDP_ORDER_CHANGE)
+ {
+ out_uint8(self->out_s, self->last_order)
+ }
+ present = 0;
+ present_ptr = self->out_s->p; /* hold 1 byte present pointer */
+ out_uint8s(self->out_s, 1)
+ if ((order_flags & RDP_ORDER_BOUNDS) &&
+ !(order_flags & RDP_ORDER_LASTBOUNDS))
+ {
+ xrdp_orders_out_bounds(self, rect);
+ }
+ if (x != self->dest_blt_x)
+ {
+ present |= 0x01;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, x - self->dest_blt_x);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, x);
+ }
+ self->dest_blt_x = x;
+ }
+ if (y != self->dest_blt_y)
+ {
+ present |= 0x02;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, y - self->dest_blt_y);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, y);
+ }
+ self->dest_blt_y = y;
+ }
+ if (cx != self->dest_blt_cx)
+ {
+ present |= 0x04;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, cx - self->dest_blt_cx);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, cx);
+ }
+ self->dest_blt_cx = cx;
+ }
+ if (cy != self->dest_blt_cy)
+ {
+ present |= 0x08;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, cy - self->dest_blt_cy);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, cy);
+ }
+ self->dest_blt_cy = cy;
+ }
+ if (rop != self->dest_blt_rop)
+ {
+ present |= 0x10;
+ out_uint8(self->out_s, rop);
+ self->dest_blt_rop = rop;
+ }
+ present_ptr[0] = present;
+ return 0;
+}
+
+/*****************************************************************************/
+/* returns error */
+/* send a line order */
+/* max size 32 */
+int APP_CC
+xrdp_orders_line(struct xrdp_orders* self, int mix_mode,
+ int startx, int starty,
+ int endx, int endy, int rop, int bg_color,
+ struct xrdp_pen* pen,
+ struct xrdp_rect* rect)
+{
+ int order_flags;
+ int vals[8];
+ int present;
+ char* present_ptr;
+ struct xrdp_pen blank_pen;
+
+ xrdp_orders_check(self, 32);
+ self->order_count++;
+ order_flags = RDP_ORDER_STANDARD;
+ if (self->last_order != RDP_ORDER_LINE)
+ {
+ order_flags |= RDP_ORDER_CHANGE;
+ }
+ self->last_order = RDP_ORDER_LINE;
+ if (rect != 0)
+ {
+ order_flags |= RDP_ORDER_BOUNDS;
+ if (xrdp_orders_last_bounds(self, rect))
+ {
+ order_flags |= RDP_ORDER_LASTBOUNDS;
+ }
+ }
+ vals[0] = startx;
+ vals[1] = self->line_startx;
+ vals[2] = starty;
+ vals[3] = self->line_starty;
+ vals[4] = endx;
+ vals[5] = self->line_endx;
+ vals[6] = endy;
+ vals[7] = self->line_endy;
+ if (xrdp_orders_send_delta(self, vals, 8))
+ {
+ order_flags |= RDP_ORDER_DELTA;
+ }
+ out_uint8(self->out_s, order_flags);
+ if (order_flags & RDP_ORDER_CHANGE)
+ {
+ out_uint8(self->out_s, self->last_order);
+ }
+ present = 0;
+ present_ptr = self->out_s->p; /* hold 2 byte present pointer */
+ out_uint8s(self->out_s, 2)
+ if ((order_flags & RDP_ORDER_BOUNDS) &&
+ !(order_flags & RDP_ORDER_LASTBOUNDS))
+ {
+ xrdp_orders_out_bounds(self, rect);
+ }
+ if (mix_mode != self->line_mix_mode)
+ {
+ present |= 0x0001;
+ out_uint16_le(self->out_s, mix_mode)
+ self->line_mix_mode = mix_mode;
+ }
+ if (startx != self->line_startx)
+ {
+ present |= 0x0002;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, startx - self->line_startx);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, startx);
+ }
+ self->line_startx = startx;
+ }
+ if (starty != self->line_starty)
+ {
+ present |= 0x0004;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, starty - self->line_starty);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, starty);
+ }
+ self->line_starty = starty;
+ }
+ if (endx != self->line_endx)
+ {
+ present |= 0x0008;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, endx - self->line_endx);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, endx);
+ }
+ self->line_endx = endx;
+ }
+ if (endy != self->line_endy)
+ {
+ present |= 0x0010;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, endy - self->line_endy);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, endy);
+ }
+ self->line_endy = endy;
+ }
+ if (bg_color != self->line_bg_color)
+ {
+ present |= 0x0020;
+ out_uint8(self->out_s, bg_color)
+ out_uint8(self->out_s, bg_color >> 8)
+ out_uint8(self->out_s, bg_color >> 16)
+ self->line_bg_color = bg_color;
+ }
+ if (rop != self->line_rop)
+ {
+ present |= 0x0040;
+ out_uint8(self->out_s, rop)
+ self->line_rop = rop;
+ }
+ if (pen == 0)
+ {
+ g_memset(&blank_pen, 0, sizeof(struct xrdp_pen));
+ pen = &blank_pen;
+ }
+ if (pen->style != self->line_pen.style)
+ {
+ present |= 0x0080;
+ out_uint8(self->out_s, pen->style)
+ self->line_pen.style = pen->style;
+ }
+ if (pen->width != self->line_pen.width)
+ {
+ present |= 0x0100;
+ out_uint8(self->out_s, pen->width)
+ self->line_pen.width = pen->width;
+ }
+ if (pen->color != self->line_pen.color)
+ {
+ present |= 0x0200;
+ out_uint8(self->out_s, pen->color)
+ out_uint8(self->out_s, pen->color >> 8)
+ out_uint8(self->out_s, pen->color >> 16)
+ self->line_pen.color = pen->color;
+ }
+ present_ptr[0] = present;
+ present_ptr[1] = present >> 8;
+ return 0;
+}
+
+/*****************************************************************************/
+/* returns error */
+/* send a mem blt order */
+/* max size 30 */
+int APP_CC
+xrdp_orders_mem_blt(struct xrdp_orders* self, int cache_id,
+ int color_table, int x, int y, int cx, int cy,
+ int rop, int srcx, int srcy,
+ int cache_idx, struct xrdp_rect* rect)
+{
+ int order_flags;
+ int vals[12];
+ int present;
+ char* present_ptr;
+
+ xrdp_orders_check(self, 30);
+ self->order_count++;
+ order_flags = RDP_ORDER_STANDARD;
+ if (self->last_order != RDP_ORDER_MEMBLT)
+ {
+ order_flags |= RDP_ORDER_CHANGE;
+ }
+ self->last_order = RDP_ORDER_MEMBLT;
+ if (rect != 0)
+ {
+ order_flags |= RDP_ORDER_BOUNDS;
+ if (xrdp_orders_last_bounds(self, rect))
+ {
+ order_flags |= RDP_ORDER_LASTBOUNDS;
+ }
+ }
+ vals[0] = x;
+ vals[1] = self->mem_blt_x;
+ vals[2] = y;
+ vals[3] = self->mem_blt_y;
+ vals[4] = cx;
+ vals[5] = self->mem_blt_cx;
+ vals[6] = cy;
+ vals[7] = self->mem_blt_cy;
+ vals[8] = srcx;
+ vals[9] = self->mem_blt_srcx;
+ vals[10] = srcy;
+ vals[11] = self->mem_blt_srcy;
+ if (xrdp_orders_send_delta(self, vals, 12))
+ {
+ order_flags |= RDP_ORDER_DELTA;
+ }
+ out_uint8(self->out_s, order_flags);
+ if (order_flags & RDP_ORDER_CHANGE)
+ {
+ out_uint8(self->out_s, self->last_order)
+ }
+ present = 0;
+ present_ptr = self->out_s->p; /* hold 2 byte present pointer, todo */
+ out_uint8s(self->out_s, 2) /* this can be smaller, */
+ /* see RDP_ORDER_SMALL and RDP_ORDER_TINY */
+ if ((order_flags & RDP_ORDER_BOUNDS) &&
+ !(order_flags & RDP_ORDER_LASTBOUNDS))
+ {
+ xrdp_orders_out_bounds(self, rect);
+ }
+ if (cache_id != self->mem_blt_cache_id ||
+ color_table != self->mem_blt_color_table)
+ {
+ present |= 0x0001;
+ out_uint8(self->out_s, cache_id);
+ out_uint8(self->out_s, color_table);
+ self->mem_blt_cache_id = cache_id;
+ self->mem_blt_color_table = color_table;
+ }
+ if (x != self->mem_blt_x)
+ {
+ present |= 0x0002;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, x - self->mem_blt_x);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, x);
+ }
+ self->mem_blt_x = x;
+ }
+ if (y != self->mem_blt_y)
+ {
+ present |= 0x0004;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, y - self->mem_blt_y);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, y);
+ }
+ self->mem_blt_y = y;
+ }
+ if (cx != self->mem_blt_cx)
+ {
+ present |= 0x0008;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, cx - self->mem_blt_cx);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, cx);
+ }
+ self->mem_blt_cx = cx;
+ }
+ if (cy != self->mem_blt_cy)
+ {
+ present |= 0x0010;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, cy - self->mem_blt_cy);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, cy);
+ }
+ self->mem_blt_cy = cy;
+ }
+ if (rop != self->mem_blt_rop)
+ {
+ present |= 0x0020;
+ out_uint8(self->out_s, rop);
+ self->mem_blt_rop = rop;
+ }
+ if (srcx != self->mem_blt_srcx)
+ {
+ present |= 0x0040;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, srcx - self->mem_blt_srcx);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, srcx);
+ }
+ self->mem_blt_srcx = srcx;
+ }
+ if (srcy != self->mem_blt_srcy)
+ {
+ present |= 0x0080;
+ if (order_flags & RDP_ORDER_DELTA)
+ {
+ out_uint8(self->out_s, srcy - self->mem_blt_srcy);
+ }
+ else
+ {
+ out_uint16_le(self->out_s, srcy);
+ }
+ self->mem_blt_srcy = srcy;
+ }
+ if (cache_idx != self->mem_blt_cache_idx)
+ {
+ present |= 0x0100;
+ out_uint16_le(self->out_s, cache_idx);
+ self->mem_blt_cache_idx = cache_idx;
+ }
+ present_ptr[0] = present;
+ present_ptr[1] = present >> 8;
+ return 0;
+}
+
+/*****************************************************************************/
+/* returns error */
+int APP_CC
+xrdp_orders_text(struct xrdp_orders* self,
+ int font, int flags, int mixmode,
+ int fg_color, int bg_color,
+ int clip_left, int clip_top,
+ int clip_right, int clip_bottom,
+ int box_left, int box_top,
+ int box_right, int box_bottom,
+ int x, int y, char* data, int data_len,
+ struct xrdp_rect* rect)
+{
+ int order_flags;
+ int present;
+ char* present_ptr;
+
+ xrdp_orders_check(self, 100);
+ self->order_count++;
+ order_flags = RDP_ORDER_STANDARD;
+ if (self->last_order != RDP_ORDER_TEXT2)
+ {
+ order_flags |= RDP_ORDER_CHANGE;
+ }
+ self->last_order = RDP_ORDER_TEXT2;
+ if (rect != 0)
+ {
+ order_flags |= RDP_ORDER_BOUNDS;
+ if (xrdp_orders_last_bounds(self, rect))
+ {
+ order_flags |= RDP_ORDER_LASTBOUNDS;
+ }
+ }
+ out_uint8(self->out_s, order_flags);
+ if (order_flags & RDP_ORDER_CHANGE)
+ {
+ out_uint8(self->out_s, self->last_order);
+ }
+ present = 0;
+ present_ptr = self->out_s->p; /* hold 3 byte present pointer, todo */
+ out_uint8s(self->out_s, 3) /* this can be smaller, */
+ /* see RDP_ORDER_SMALL and RDP_ORDER_TINY */
+ if ((order_flags & RDP_ORDER_BOUNDS) &&
+ !(order_flags & RDP_ORDER_LASTBOUNDS))
+ {
+ xrdp_orders_out_bounds(self, rect);
+ }
+ if (font != self->text_font)
+ {
+ present |= 0x000001;
+ out_uint8(self->out_s, font);
+ self->text_font = font;
+ }
+ if (flags != self->text_flags)
+ {
+ present |= 0x000002;
+ out_uint8(self->out_s, flags);
+ self->text_flags = flags;
+ }
+ /* unknown */
+ if (mixmode != self->text_mixmode)
+ {
+ present |= 0x000008;
+ out_uint8(self->out_s, mixmode);
+ self->text_mixmode = mixmode;
+ }
+ if (fg_color != self->text_fg_color)
+ {
+ present |= 0x000010;
+ out_uint8(self->out_s, fg_color)
+ out_uint8(self->out_s, fg_color >> 8)
+ out_uint8(self->out_s, fg_color >> 16)
+ self->text_fg_color = fg_color;
+ }
+ if (bg_color != self->text_bg_color)
+ {
+ present |= 0x000020;
+ out_uint8(self->out_s, bg_color)
+ out_uint8(self->out_s, bg_color >> 8)
+ out_uint8(self->out_s, bg_color >> 16)
+ self->text_bg_color = bg_color;
+ }
+ if (clip_left != self->text_clip_left)
+ {
+ present |= 0x000040;
+ out_uint16_le(self->out_s, clip_left);
+ self->text_clip_left = clip_left;
+ }
+ if (clip_top != self->text_clip_top)
+ {
+ present |= 0x000080;
+ out_uint16_le(self->out_s, clip_top);
+ self->text_clip_top = clip_top;
+ }
+ if (clip_right != self->text_clip_right)
+ {
+ present |= 0x000100;
+ out_uint16_le(self->out_s, clip_right);
+ self->text_clip_right = clip_right;
+ }
+ if (clip_bottom != self->text_clip_bottom)
+ {
+ present |= 0x000200;
+ out_uint16_le(self->out_s, clip_bottom);
+ self->text_clip_bottom = clip_bottom;
+ }
+ if (box_left != self->text_box_left)
+ {
+ present |= 0x000400;
+ out_uint16_le(self->out_s, box_left);
+ self->text_box_left = box_left;
+ }
+ if (box_top != self->text_box_top)
+ {
+ present |= 0x000800;
+ out_uint16_le(self->out_s, box_top);
+ self->text_box_top = box_top;
+ }
+ if (box_right != self->text_box_right)
+ {
+ present |= 0x001000;
+ out_uint16_le(self->out_s, box_right);
+ self->text_box_right = box_right;
+ }
+ if (box_bottom != self->text_box_bottom)
+ {
+ present |= 0x002000;
+ out_uint16_le(self->out_s, box_bottom);
+ self->text_box_bottom = box_bottom;
+ }
+ if (x != self->text_x)
+ {
+ present |= 0x080000;
+ out_uint16_le(self->out_s, x);
+ self->text_x = x;
+ }
+ if (y != self->text_y)
+ {
+ present |= 0x100000;
+ out_uint16_le(self->out_s, y);
+ self->text_y = y;
+ }
+ {
+ /* always send text */
+ present |= 0x200000;
+ out_uint8(self->out_s, data_len);
+ out_uint8a(self->out_s, data, data_len);
+ }
+ /* send 3 byte present */
+ present_ptr[0] = present;
+ present_ptr[1] = present >> 8;
+ present_ptr[2] = present >> 16;
+ return 0;
+}
+
+/*****************************************************************************/
+/* returns error */
+/* when a palette gets sent, send the main palette too */
+int APP_CC
+xrdp_orders_send_palette(struct xrdp_orders* self, int* palette,
+ int cache_id)
+{
+ int order_flags;
+ int len;
+ int i;
+
+ xrdp_orders_check(self, 2000);
+ self->order_count++;
+ order_flags = RDP_ORDER_STANDARD | RDP_ORDER_SECONDARY;
+ out_uint8(self->out_s, order_flags);
+ len = 1027 - 7; /* length after type minus 7 */
+ out_uint16_le(self->out_s, len);
+ out_uint16_le(self->out_s, 0); /* flags */
+ out_uint8(self->out_s, RDP_ORDER_COLCACHE); /* type */
+ out_uint8(self->out_s, cache_id);
+ out_uint16_le(self->out_s, 256); /* num colors */
+ for (i = 0; i < 256; i++)
+ {
+ out_uint8(self->out_s, palette[i]);
+ out_uint8(self->out_s, palette[i] >> 8);
+ out_uint8(self->out_s, palette[i] >> 16);
+ out_uint8(self->out_s, 0);
+ }
+ return 0;
+}
+
+/*****************************************************************************/
+/* returns error */
+/* max size width * height * Bpp + 16 */
+int APP_CC
+xrdp_orders_send_raw_bitmap(struct xrdp_orders* self,
+ int width, int height, int bpp, char* data,
+ int cache_id, int cache_idx)
+{
+ int order_flags;
+ int len;
+ int bufsize;
+ int Bpp;
+ int i;
+ int j;
+ int pixel;
+ int e;
+
+ e = width % 4;
+ if (e != 0)
+ {
+ e = 4 - e;
+ }
+ Bpp = (bpp + 7) / 8;
+ bufsize = (width + e) * height * Bpp;
+ xrdp_orders_check(self, bufsize + 16);
+ self->order_count++;
+ order_flags = RDP_ORDER_STANDARD | RDP_ORDER_SECONDARY;
+ out_uint8(self->out_s, order_flags);
+ len = (bufsize + 9) - 7; /* length after type minus 7 */
+ out_uint16_le(self->out_s, len);
+ out_uint16_le(self->out_s, 8); /* flags */
+ out_uint8(self->out_s, RDP_ORDER_RAW_BMPCACHE); /* type */
+ out_uint8(self->out_s, cache_id);
+ out_uint8s(self->out_s, 1); /* pad */
+ out_uint8(self->out_s, width + e);
+ out_uint8(self->out_s, height);
+ out_uint8(self->out_s, bpp);
+ out_uint16_le(self->out_s, bufsize);
+ out_uint16_le(self->out_s, cache_idx);
+ for (i = height - 1; i >= 0; i--)
+ {
+ for (j = 0; j < width; j++)
+ {
+ if (Bpp == 3)
+ {
+ pixel = GETPIXEL32(data, j, i, width);
+ out_uint8(self->out_s, pixel >> 16);
+ out_uint8(self->out_s, pixel >> 8);
+ out_uint8(self->out_s, pixel);
+ }
+ else if (Bpp == 2)
+ {
+ pixel = GETPIXEL16(data, j, i, width);
+ out_uint8(self->out_s, pixel);
+ out_uint8(self->out_s, pixel >> 8);
+ }
+ else if (Bpp == 1)
+ {
+ pixel = GETPIXEL8(data, j, i, width);
+ out_uint8(self->out_s, pixel);
+ }
+ }
+ for (j = 0; j < e; j++)
+ {
+ out_uint8s(self->out_s, Bpp);
+ }
+ }
+ return 0;
+}
+
+/*****************************************************************************/
+/* returns error */
+/* max size width * height * Bpp + 16 */
+int APP_CC
+xrdp_orders_send_bitmap(struct xrdp_orders* self,
+ int width, int height, int bpp, char* data,
+ int cache_id, int cache_idx)
+{
+ int order_flags;
+ int len;
+ int bufsize;
+ int Bpp;
+ int i;
+ int lines_sending;
+ int e;
+ struct stream* s;
+ struct stream* temp_s;
+ char* p;
+
+ if (width > 64)
+ {
+ g_printf("error, width > 64\n\r");
+ return 1;
+ }
+ if (height > 64)
+ {
+ g_printf("error, height > 64\n\r");
+ return 1;
+ }
+ e = width % 4;
+ if (e != 0)
+ {
+ e = 4 - e;
+ }
+ make_stream(s);
+ init_stream(s, 8192);
+ make_stream(temp_s);
+ init_stream(temp_s, 8192);
+ p = s->p;
+ i = height;
+ lines_sending = xrdp_bitmap_compress(data, width, height, s, bpp, 8192,
+ i - 1, temp_s, e);
+ if (lines_sending != height)
+ {
+ free_stream(s);
+ free_stream(temp_s);
+ g_printf("error in xrdp_orders_send_bitmap, lines_sending(%d) != \
+height(%d)\n\r", lines_sending, height);
+ return 1;
+ }
+ bufsize = s->p - p;
+ Bpp = (bpp + 7) / 8;
+ xrdp_orders_check(self, bufsize + 16);
+ self->order_count++;
+ order_flags = RDP_ORDER_STANDARD | RDP_ORDER_SECONDARY;
+ out_uint8(self->out_s, order_flags);
+ len = (bufsize + 9 + 8) - 7; /* length after type minus 7 */
+ out_uint16_le(self->out_s, len);
+ out_uint16_le(self->out_s, 8); /* flags */
+ out_uint8(self->out_s, RDP_ORDER_BMPCACHE); /* type */
+ out_uint8(self->out_s, cache_id);
+ out_uint8s(self->out_s, 1); /* pad */
+ out_uint8(self->out_s, width + e);
+ out_uint8(self->out_s, height);
+ out_uint8(self->out_s, bpp);
+ out_uint16_le(self->out_s, bufsize + 8);
+ out_uint16_le(self->out_s, cache_idx);
+ out_uint8s(self->out_s, 2); /* pad */
+ out_uint16_le(self->out_s, bufsize);
+ out_uint16_le(self->out_s, (width + e) * Bpp); /* line size */
+ out_uint16_le(self->out_s, (width + e) *
+ Bpp * height); /* final size */
+ out_uint8a(self->out_s, s->data, bufsize);
+ free_stream(s);
+ free_stream(temp_s);
+ return 0;
+}
+
+/*****************************************************************************/
+/* returns error */
+/* max size datasize + 18*/
+/* todo, only sends one for now */
+int APP_CC
+xrdp_orders_send_font(struct xrdp_orders* self,
+ struct xrdp_font_char* font_char,
+ int font_index, int char_index)
+{
+ int order_flags;
+ int datasize;
+ int len;
+
+ datasize = FONT_DATASIZE(font_char);
+ xrdp_orders_check(self, datasize + 18);
+ self->order_count++;
+ order_flags = RDP_ORDER_STANDARD | RDP_ORDER_SECONDARY;
+ out_uint8(self->out_s, order_flags);
+ len = (datasize + 12) - 7; /* length after type minus 7 */
+ out_uint16_le(self->out_s, len);
+ out_uint16_le(self->out_s, 8); /* flags */
+ out_uint8(self->out_s, RDP_ORDER_FONTCACHE); /* type */
+ out_uint8(self->out_s, font_index);
+ out_uint8(self->out_s, 1); /* num of chars */
+ out_uint16_le(self->out_s, char_index);
+ out_uint16_le(self->out_s, font_char->offset);
+ out_uint16_le(self->out_s, font_char->baseline);
+ out_uint16_le(self->out_s, font_char->width);
+ out_uint16_le(self->out_s, font_char->height);
+ out_uint8a(self->out_s, font_char->data, datasize);
+ return 0;
+}