Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 1 | /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. |
Pierre Ossman | 6a1a0d0 | 2017-02-19 15:48:17 +0100 | [diff] [blame^] | 2 | * Copyright 2009-2017 Pierre Ossman for Cendio AB |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 3 | * |
| 4 | * This is free software; you can redistribute it and/or modify |
| 5 | * it under the terms of the GNU General Public License as published by |
| 6 | * the Free Software Foundation; either version 2 of the License, or |
| 7 | * (at your option) any later version. |
| 8 | * |
| 9 | * This software is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU General Public License |
| 15 | * along with this software; if not, write to the Free Software |
| 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
| 17 | * USA. |
| 18 | */ |
| 19 | #include <stdio.h> |
Pierre Ossman | 7638e9c | 2014-01-16 13:12:40 +0100 | [diff] [blame] | 20 | #include <rfb/msgTypes.h> |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 21 | #include <rdr/InStream.h> |
| 22 | #include <rfb/Exception.h> |
| 23 | #include <rfb/util.h> |
| 24 | #include <rfb/CMsgHandler.h> |
| 25 | #include <rfb/CMsgReader.h> |
| 26 | |
| 27 | using namespace rfb; |
| 28 | |
| 29 | CMsgReader::CMsgReader(CMsgHandler* handler_, rdr::InStream* is_) |
| 30 | : imageBufIdealSize(0), handler(handler_), is(is_), |
Pierre Ossman | 7bfb73b | 2015-11-10 13:03:26 +0100 | [diff] [blame] | 31 | nUpdateRectsLeft(0) |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 32 | { |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 33 | } |
| 34 | |
| 35 | CMsgReader::~CMsgReader() |
| 36 | { |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 37 | } |
| 38 | |
Pierre Ossman | 7638e9c | 2014-01-16 13:12:40 +0100 | [diff] [blame] | 39 | void CMsgReader::readServerInit() |
| 40 | { |
| 41 | int width = is->readU16(); |
| 42 | int height = is->readU16(); |
| 43 | handler->setDesktopSize(width, height); |
| 44 | PixelFormat pf; |
| 45 | pf.read(is); |
| 46 | handler->setPixelFormat(pf); |
| 47 | CharArray name(is->readString()); |
| 48 | handler->setName(name.buf); |
| 49 | handler->serverInit(); |
| 50 | } |
| 51 | |
| 52 | void CMsgReader::readMsg() |
| 53 | { |
| 54 | if (nUpdateRectsLeft == 0) { |
| 55 | int type = is->readU8(); |
| 56 | |
| 57 | switch (type) { |
| 58 | case msgTypeSetColourMapEntries: |
| 59 | readSetColourMapEntries(); |
| 60 | break; |
| 61 | case msgTypeBell: |
| 62 | readBell(); |
| 63 | break; |
| 64 | case msgTypeServerCutText: |
| 65 | readServerCutText(); |
| 66 | break; |
| 67 | case msgTypeFramebufferUpdate: |
| 68 | readFramebufferUpdate(); |
| 69 | break; |
| 70 | case msgTypeServerFence: |
| 71 | readFence(); |
| 72 | break; |
| 73 | case msgTypeEndOfContinuousUpdates: |
| 74 | readEndOfContinuousUpdates(); |
| 75 | break; |
| 76 | default: |
| 77 | fprintf(stderr, "unknown message type %d\n", type); |
| 78 | throw Exception("unknown message type"); |
| 79 | } |
| 80 | } else { |
| 81 | int x = is->readU16(); |
| 82 | int y = is->readU16(); |
| 83 | int w = is->readU16(); |
| 84 | int h = is->readU16(); |
| 85 | int encoding = is->readS32(); |
| 86 | |
| 87 | switch (encoding) { |
| 88 | case pseudoEncodingLastRect: |
| 89 | nUpdateRectsLeft = 1; // this rectangle is the last one |
| 90 | break; |
| 91 | case pseudoEncodingCursor: |
| 92 | readSetCursor(w, h, Point(x,y)); |
| 93 | break; |
| 94 | case pseudoEncodingDesktopName: |
| 95 | readSetDesktopName(x, y, w, h); |
| 96 | break; |
| 97 | case pseudoEncodingDesktopSize: |
| 98 | handler->setDesktopSize(w, h); |
| 99 | break; |
| 100 | case pseudoEncodingExtendedDesktopSize: |
| 101 | readExtendedDesktopSize(x, y, w, h); |
| 102 | break; |
| 103 | default: |
| 104 | readRect(Rect(x, y, x+w, y+h), encoding); |
| 105 | break; |
| 106 | }; |
| 107 | |
| 108 | nUpdateRectsLeft--; |
| 109 | if (nUpdateRectsLeft == 0) |
| 110 | handler->framebufferUpdateEnd(); |
| 111 | } |
| 112 | } |
| 113 | |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 114 | void CMsgReader::readSetColourMapEntries() |
| 115 | { |
| 116 | is->skip(1); |
| 117 | int firstColour = is->readU16(); |
| 118 | int nColours = is->readU16(); |
| 119 | rdr::U16Array rgbs(nColours * 3); |
| 120 | for (int i = 0; i < nColours * 3; i++) |
| 121 | rgbs.buf[i] = is->readU16(); |
| 122 | handler->setColourMapEntries(firstColour, nColours, rgbs.buf); |
| 123 | } |
| 124 | |
| 125 | void CMsgReader::readBell() |
| 126 | { |
| 127 | handler->bell(); |
| 128 | } |
| 129 | |
| 130 | void CMsgReader::readServerCutText() |
| 131 | { |
| 132 | is->skip(3); |
Adam Tkac | acf6c6b | 2009-02-13 12:42:05 +0000 | [diff] [blame] | 133 | rdr::U32 len = is->readU32(); |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 134 | if (len > 256*1024) { |
| 135 | is->skip(len); |
| 136 | fprintf(stderr,"cut text too long (%d bytes) - ignoring\n",len); |
| 137 | return; |
| 138 | } |
| 139 | CharArray ca(len+1); |
| 140 | ca.buf[len] = 0; |
| 141 | is->readBytes(ca.buf, len); |
| 142 | handler->serverCutText(ca.buf, len); |
| 143 | } |
| 144 | |
Pierre Ossman | 7638e9c | 2014-01-16 13:12:40 +0100 | [diff] [blame] | 145 | void CMsgReader::readFence() |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 146 | { |
Pierre Ossman | 7638e9c | 2014-01-16 13:12:40 +0100 | [diff] [blame] | 147 | rdr::U32 flags; |
| 148 | rdr::U8 len; |
| 149 | char data[64]; |
| 150 | |
| 151 | is->skip(3); |
| 152 | |
| 153 | flags = is->readU32(); |
| 154 | |
| 155 | len = is->readU8(); |
| 156 | if (len > sizeof(data)) { |
| 157 | fprintf(stderr, "Ignoring fence with too large payload\n"); |
| 158 | is->skip(len); |
| 159 | return; |
| 160 | } |
| 161 | |
| 162 | is->readBytes(data, len); |
| 163 | |
| 164 | handler->fence(flags, len, data); |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 165 | } |
| 166 | |
Pierre Ossman | 7638e9c | 2014-01-16 13:12:40 +0100 | [diff] [blame] | 167 | void CMsgReader::readEndOfContinuousUpdates() |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 168 | { |
Pierre Ossman | 7638e9c | 2014-01-16 13:12:40 +0100 | [diff] [blame] | 169 | handler->endOfContinuousUpdates(); |
| 170 | } |
| 171 | |
| 172 | void CMsgReader::readFramebufferUpdate() |
| 173 | { |
| 174 | is->skip(1); |
| 175 | nUpdateRectsLeft = is->readU16(); |
| 176 | handler->framebufferUpdateStart(); |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 177 | } |
| 178 | |
Peter Åstrand | 98fe98c | 2010-02-10 07:43:02 +0000 | [diff] [blame] | 179 | void CMsgReader::readRect(const Rect& r, int encoding) |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 180 | { |
| 181 | if ((r.br.x > handler->cp.width) || (r.br.y > handler->cp.height)) { |
| 182 | fprintf(stderr, "Rect too big: %dx%d at %d,%d exceeds %dx%d\n", |
| 183 | r.width(), r.height(), r.tl.x, r.tl.y, |
| 184 | handler->cp.width, handler->cp.height); |
| 185 | throw Exception("Rect too big"); |
| 186 | } |
| 187 | |
| 188 | if (r.is_empty()) |
| 189 | fprintf(stderr, "Warning: zero size rect\n"); |
| 190 | |
Pierre Ossman | fdba3fe | 2014-01-31 13:12:18 +0100 | [diff] [blame] | 191 | handler->dataRect(r, encoding); |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 192 | } |
| 193 | |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 194 | void CMsgReader::readSetCursor(int width, int height, const Point& hotspot) |
| 195 | { |
| 196 | int data_len = width * height * (handler->cp.pf().bpp/8); |
| 197 | int mask_len = ((width+7)/8) * height; |
| 198 | rdr::U8Array data(data_len); |
| 199 | rdr::U8Array mask(mask_len); |
| 200 | |
Pierre Ossman | 6a1a0d0 | 2017-02-19 15:48:17 +0100 | [diff] [blame^] | 201 | int x, y; |
| 202 | rdr::U8 buf[width*height*4]; |
| 203 | rdr::U8* in; |
| 204 | rdr::U8* out; |
| 205 | |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 206 | is->readBytes(data.buf, data_len); |
| 207 | is->readBytes(mask.buf, mask_len); |
| 208 | |
Pierre Ossman | 6a1a0d0 | 2017-02-19 15:48:17 +0100 | [diff] [blame^] | 209 | int maskBytesPerRow = (width+7)/8; |
| 210 | in = data.buf; |
| 211 | out = buf; |
| 212 | for (y = 0;y < height;y++) { |
| 213 | for (x = 0;x < width;x++) { |
| 214 | int byte = y * maskBytesPerRow + x / 8; |
| 215 | int bit = 7 - x % 8; |
| 216 | |
| 217 | handler->cp.pf().rgbFromBuffer(out, in, 1); |
| 218 | |
| 219 | if (mask.buf[byte] & (1 << bit)) |
| 220 | out[3] = 255; |
| 221 | else |
| 222 | out[3] = 0; |
| 223 | |
| 224 | in += handler->cp.pf().bpp/8; |
| 225 | out += 4; |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | handler->setCursor(width, height, hotspot, buf); |
Constantin Kaplinsky | a2adc8d | 2006-05-25 05:01:55 +0000 | [diff] [blame] | 230 | } |
| 231 | |
Pierre Ossman | 7638e9c | 2014-01-16 13:12:40 +0100 | [diff] [blame] | 232 | void CMsgReader::readSetDesktopName(int x, int y, int w, int h) |
| 233 | { |
| 234 | char* name = is->readString(); |
| 235 | |
| 236 | if (x || y || w || h) { |
| 237 | fprintf(stderr, "Ignoring DesktopName rect with non-zero position/size\n"); |
| 238 | } else { |
| 239 | handler->setName(name); |
| 240 | } |
| 241 | |
| 242 | delete [] name; |
| 243 | } |
| 244 | |
| 245 | void CMsgReader::readExtendedDesktopSize(int x, int y, int w, int h) |
| 246 | { |
| 247 | unsigned int screens, i; |
| 248 | rdr::U32 id, flags; |
| 249 | int sx, sy, sw, sh; |
| 250 | ScreenSet layout; |
| 251 | |
| 252 | screens = is->readU8(); |
| 253 | is->skip(3); |
| 254 | |
| 255 | for (i = 0;i < screens;i++) { |
| 256 | id = is->readU32(); |
| 257 | sx = is->readU16(); |
| 258 | sy = is->readU16(); |
| 259 | sw = is->readU16(); |
| 260 | sh = is->readU16(); |
| 261 | flags = is->readU32(); |
| 262 | |
| 263 | layout.add_screen(Screen(id, sx, sy, sw, sh, flags)); |
| 264 | } |
| 265 | |
| 266 | handler->setExtendedDesktopSize(x, y, w, h, layout); |
| 267 | } |