VNCviewGS/vncdisplay.cc

385 lines
12 KiB
C++

#if __ORCAC__
#pragma lint -1
#pragma noroot
segment "VNCview GS";
#endif
#include <window.h>
#include <quickdraw.h>
#include <qdaux.h>
#include <desk.h>
#include <memory.h>
#include <resources.h>
#include <tcpip.h>
#include <menu.h>
#include <control.h>
#include <misctool.h>
#include <scrap.h>
#include <stdio.h>
#include <stdlib.h>
#include <event.h>
#include <limits.h>
#include <orca.h>
#include "vncsession.h"
#include "vncview.h"
#include "vncdisplay.h"
#include "colortables.h"
#include "readtcp.h"
#include "menus.h"
#include "clipboard.h"
#include "desktopsize.h"
#include "mouse.h"
#include "keyboard.h"
#include "copyrect.h"
#include "raw.h"
#include "hextile.h"
unsigned int fbHeight;
unsigned int fbWidth;
BOOLEAN displayInProgress;
static BOOLEAN peekedNextMsg;
static unsigned int numRects;
unsigned int rectX;
unsigned int rectY;
unsigned int rectWidth;
unsigned int rectHeight;
static unsigned long rectEncoding;
GrafPortPtr vncWindow;
/* VNC session window dimensions */
unsigned int winHeight;
unsigned int winWidth;
/* On the next 2 structs, only certain values are permanently zero.
* Others are changed later.
*/
struct LocInfo srcLocInfo = {0, 0, 0, {0, 0, 0, 0} };
/* Used by multiple encodings */
Rect srcRect = {0, 0, 0, 0};
unsigned char *pixTransTbl;
BOOLEAN checkBounds = FALSE; /* Adjust drawing to stay in bounds */
unsigned long skipBytes = 0;
/* Server-to-client message types */
#define FBUpdate 0
#define SetColourMapEntries 1
#define Bell 2
#define ServerCutText 3
#define txtColor 10
#define txtGray 11
#define txtTransfers 23
/* Send a request to be sent the data to redraw the window when part of it has
* been erased. It will be a while before the data is fully redrawn, but this
* is an unavoidable penalty of opening other windows over the main VNC window.
*/
#pragma databank 1
static void VNCRedraw (void) {
RegionHndl updateRgnHndl;
updateRgnHndl = vncWindow->visRgn;
SendFBUpdateRequest(FALSE,
(**updateRgnHndl).rgnBBox.h1,
(**updateRgnHndl).rgnBBox.v1,
(**updateRgnHndl).rgnBBox.h2 - (**updateRgnHndl).rgnBBox.h1,
(**updateRgnHndl).rgnBBox.v2 - (**updateRgnHndl).rgnBBox.v1);
checkBounds = TRUE;
}
#pragma databank 0
/* Change Super Hi-Res display to the specified resolution (640 or 320).
* Uses the procedure described in IIgs Tech Note #4.
*/
static void ChangeResolution(int rez) {
static Handle dpSpace;
unsigned int masterSCB;
hRez = rez;
winHeight = WIN_HEIGHT;
winWidth = (rez == 640) ? WIN_WIDTH_640 : WIN_WIDTH_320;
/* Set up pixel translation table for correct graphics mode */
if (rez == 320)
pixTransTbl = coltab320;
else /* 640 mode */
pixTransTbl = coltab640;
srcLocInfo.portSCB = (rez == 640) ? 0x87 : 0x00;
/* Check if we need to change modes */
masterSCB = GetMasterSCB();
if ( ( (masterSCB & 0x80) && (rez == 640)) ||
(!(masterSCB & 0x80) && (rez == 320)) ) {
return; /* Already in right mode, so don't change things */
}
/* Perform the basic procedure described in IIgs TN #4,
* with some adjustments to provide a smoother transition.
*/
CloseAllNDAs();
QDAuxShutDown();
SetMasterSCB(masterSCB | 0x100); /* To leave SHR screen active */
QDShutDown();
if (dpSpace == NULL)
dpSpace = NewHandle(0x0300, userid(),
attrLocked|attrFixed|attrNoCross|attrBank, 0x00000000);
QDStartUp((Word) *dpSpace, (rez == 640) ? 0xC187 : 0xC100, 0, userid());
/* SCB 0x87 gives 640 mode with our custom gray palette */
QDAuxStartUp();
ClampMouse(0, (rez == 640) ? 639 : 319, 0, 199);
HomeMouse();
ShowCursor();
WindNewRes();
InitPalette(); /* Set up Apple menu colors before it is redrawn */
MenuNewRes();
CtlNewRes();
RefreshDesktop(NULL);
/* Position new connection window at default location for new mode */
if (rez == 320) {
MoveControl(25, 64, GetCtlHandleFromID(newConnWindow, txtColor));
MoveControl(25, 87, GetCtlHandleFromID(newConnWindow, txtGray));
MoveControl(134, 91, GetCtlHandleFromID(newConnWindow, txtTransfers));
MoveWindow(2, 35, newConnWindow);
}
else {
MoveControl(35, 64, GetCtlHandleFromID(newConnWindow, txtColor));
MoveControl(35, 87, GetCtlHandleFromID(newConnWindow, txtGray));
MoveControl(144, 91, GetCtlHandleFromID(newConnWindow, txtTransfers));
MoveWindow(162, 35, newConnWindow);
}
}
/* Display the VNC session window, first changing to the appropriate
* resolution (as specified by the user) if necessary.
*/
void InitVNCWindow(void) {
#define wrNum640 1003
#define wrNum320 1004
BOOLEAN resize = FALSE;
ChangeResolution(hRez);
vncWindow = NewWindow2(NULL, 0, NULL, NULL, 0x02,
(hRez == 640) ? wrNum640 : wrNum320,
rWindParam1);
if (fbWidth < winWidth) {
winWidth = fbWidth;
resize = TRUE;
}
if (fbHeight < winHeight) {
winHeight = fbHeight;
resize = TRUE;
}
if (resize)
SizeWindow(winWidth, winHeight, vncWindow);
SetContentDraw(VNCRedraw, vncWindow);
SetDataSize(fbWidth, fbHeight, vncWindow);
DrawControls(vncWindow);
/* We also take the opportunity here to initialize the rectangle info. */
numRects = 0;
displayInProgress = FALSE;
peekedNextMsg = FALSE;
#undef wrNum320
#undef wrNum640
}
/* Send a request to the VNC server to update the information for a portion of
* the frame buffer.
*/
void SendFBUpdateRequest (BOOLEAN incremental, unsigned int x, unsigned int y,
unsigned int width, unsigned int height) {
struct FBUpdateRequest {
unsigned char messageType;
unsigned char incremental;
unsigned int x;
unsigned int y;
unsigned int width;
unsigned int height;
} fbUpdateRequest = {3 /* Message type 3 */};
fbUpdateRequest.incremental = !!incremental;
fbUpdateRequest.x = SwapBytes2(x);
fbUpdateRequest.y = SwapBytes2(y);
fbUpdateRequest.width = SwapBytes2(width);
fbUpdateRequest.height = SwapBytes2(height);
TCPIPWriteTCP(hostIpid, &fbUpdateRequest.messageType,
sizeof(fbUpdateRequest), TRUE, FALSE);
/* No error checking here -- Can't respond to one usefully. */
}
/* Start responding to a FramebufferUpdate from the server
*/
static void DoFBUpdate (void) {
unsigned int *dataPtr; /* Pointer to header data */
if (!DoWaitingReadTCP(15)) {
DoClose(vncWindow);
//printf("Closing in DoFBUpdate\n");
return;
}
dataPtr = (unsigned int *) (readBufferPtr + 1);
numRects = SwapBytes2(dataPtr[0]); /* Get data */
rectX = SwapBytes2(dataPtr[1]);
rectY = SwapBytes2(dataPtr[2]);
rectWidth = SwapBytes2(dataPtr[3]);
rectHeight = SwapBytes2(dataPtr[4]);
rectEncoding = SwapBytes4(*(unsigned long *)(dataPtr + 5));
}
/* The server should never send a color map, since we don't use a mapped
* representation for pixel values. If a malfunctioning server tries to
* send us one, though, we read and ignore it.
*/
static void DoSetColourMapEntries (void) {
unsigned int numColors;
DoWaitingReadTCP(3);
DoWaitingReadTCP(2);
numColors = SwapBytes2(*(unsigned int *)readBufferPtr);
skipBytes = 6UL * numColors;
}
/* Skip a specified number of bytes.
*/
static void DoSkipBytes (void) {
if (DoReadTCP(skipBytes)) {
DoneWithReadBuffer();
skipBytes = 0;
}
}
/* Here when we're done processing one rectangle and ready to start the next.
* If last FramebufferUpdate had multiple rectangles, we set up for next one.
* If no more rectangles are available, we send a FramebufferUpdateRequest.
*/
void NextRect (void) {
unsigned int *dataPtr;
displayInProgress = FALSE;
numRects--;
if (numRects) { /* Process next rectangle */
if (!DoWaitingReadTCP(12)) {
//printf("Closing in NextRect\n");
DoClose(vncWindow);
return;
}
dataPtr = (unsigned int *)readBufferPtr;
rectX = SwapBytes2(dataPtr[0]);
rectY = SwapBytes2(dataPtr[1]);
rectWidth = SwapBytes2(dataPtr[2]);
rectHeight = SwapBytes2(dataPtr[3]);
rectEncoding = SwapBytes4(*(unsigned long *)(dataPtr + 4));
//printf("New Rect: X = %u, Y = %u, Width = %u, Height = %u\n", rectX, rectY, rectWidth, rectHeight);
}
else { /* No more rectangles from last update */
Origin contentOrigin;
DoneWithReadBuffer();
if (DoReadTCP(1)) {
peekedNextMsg = TRUE;
if (*readBufferPtr == FBUpdate) {
/* Don't request another FBUpdate if one is already coming.
* This helps avoid a condition where the GS may be unable
* to "catch up" with a stream of frequent updates. */
return;
}
}
contentOrigin.l = GetContentOrigin(vncWindow);
SendFBUpdateRequest(TRUE, contentOrigin.pt.h, contentOrigin.pt.v,
winWidth, winHeight);
}
}
void ConnectedEventLoop (void) {
unsigned char messageType;
if (FrontWindow() != vncWindow && menuOffset == noKB)
InitMenus(0);
else if (FrontWindow() == vncWindow && menuOffset != noKB)
InitMenus(noKB);
if (skipBytes) {
DoSkipBytes();
return;
} else if (displayInProgress) {
switch (rectEncoding) {
case encodingRaw: RawDraw();
return;
case encodingHextile: HexDispatch();
return;
case nonEncodingClipboard: GetClipboard();
return;
default: DoClose(vncWindow);
return;
}
}
else if (numRects) {
switch (rectEncoding) {
case encodingHextile:
DoHextileRect();
return;
case encodingRaw: DoRawRect();
return;
case encodingCopyRect:
DoCopyRect();
return;
case encodingDesktopSize:
DoDesktopSize();
return;
case encodingCursor:
DoCursor();
return;
default: DisplayConnectStatus (
"\pInvalid rectangle from server.", FALSE);
DoClose(vncWindow);
//printf("Closing due to bad rectangle encoding %lu\n", rectEncoding);
//printf("rectX = %u, rectY = %u, rectWidth = %u, rectHeight = %u\n", rectX, rectY, rectWidth, rectHeight);
return;
}
}
else if (peekedNextMsg || DoReadTCP(1)) { /* Read message type byte */
peekedNextMsg = FALSE;
messageType = *readBufferPtr;
switch (messageType) {
case FBUpdate: DoFBUpdate();
break;
case SetColourMapEntries: DoSetColourMapEntries();
break;
case Bell: SysBeep();
break;
case ServerCutText: rectEncoding = nonEncodingClipboard;
DoServerCutText();
break;
default: DisplayConnectStatus (
"\pInvalid message from server.",
FALSE);
DoClose(vncWindow);
return;
}
}
}