diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/Vncviewer vnc_unixsrc/vncviewer/Vncviewer --- vnc_unixsrc.orig/vncviewer/Vncviewer 2003-02-07 05:30:57.000000000 -0500 +++ vnc_unixsrc/vncviewer/Vncviewer 2008-08-24 16:26:01.000000000 -0400 @@ -1,20 +1,22 @@ ! -! Application defaults file for vncviewer. +! Application defaults file for SSVNC vncviewer. +! +! N.B.: You will need to rename this file to be "Ssvnc" instead of "Vncviewer" ! ! ! The title of the main window. "%s" will be replaced by the desktop name. -! +! -Vncviewer.title: TightVNC: %s +Ssvnc.title: SSVNC: %s Press F8 for Menu ! ! Translations on the main window. ! -Vncviewer.translations:\ +Ssvnc.translations:\ <Enter>: SelectionToVNC()\n\ <Leave>: SelectionFromVNC() @@ -23,7 +25,7 @@ ! Uncomment to grab the keyboard in full-screen mode. ! -! Vncviewer.grabKeyboard: True +! Ssvnc.grabKeyboard: True ! @@ -43,6 +45,9 @@ *viewport.useRight: True *viewport*Scrollbar*thumb: None +*viewport.horizontal.height: 6 +*viewport.vertical.width: 6 + ! ! Default translations on desktop window. @@ -50,89 +55,591 @@ *desktop.baseTranslations:\ <Key>F8: ShowPopup()\n\ + <Key>F9: ToggleFullScreen()\n\ <ButtonPress>: SendRFBEvent()\n\ <ButtonRelease>: SendRFBEvent()\n\ <Motion>: SendRFBEvent()\n\ <KeyPress>: SendRFBEvent()\n\ <KeyRelease>: SendRFBEvent() +*viewport.horizontal.translations: #override\n\ + <KeyPress>Right: StartScroll(Forward)\n\ + <KeyRelease>Right: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>Left: StartScroll(Backward)\n\ + <KeyRelease>Left: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>Next: StartScroll(Forward)\n\ + <KeyRelease>Next: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>Prior: StartScroll(Backward)\n\ + <KeyRelease>Prior: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>z: StartScroll(Forward)\n\ + <KeyRelease>z: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>a: StartScroll(Backward)\n\ + <KeyRelease>a: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>f: StartScroll(Forward)\n\ + <KeyRelease>f: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>b: StartScroll(Backward)\n\ + <KeyRelease>b: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>Down: StartScroll(Forward)\n\ + <KeyRelease>Down: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>Up: StartScroll(Backward)\n\ + <KeyRelease>Up: NotifyScroll(FullLength) EndScroll() + +*viewport.vertical.translations: #override\n\ + <KeyPress>Down: StartScroll(Forward)\n\ + <KeyRelease>Down: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>Up: StartScroll(Backward)\n\ + <KeyRelease>Up: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>Next: StartScroll(Forward)\n\ + <KeyRelease>Next: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>Prior: StartScroll(Backward)\n\ + <KeyRelease>Prior: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>z: StartScroll(Forward)\n\ + <KeyRelease>z: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>a: StartScroll(Backward)\n\ + <KeyRelease>a: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>f: StartScroll(Forward)\n\ + <KeyRelease>f: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>b: StartScroll(Backward)\n\ + <KeyRelease>b: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>Right: StartScroll(Forward)\n\ + <KeyRelease>Right: NotifyScroll(FullLength) EndScroll()\n\ + <KeyPress>Left: StartScroll(Backward)\n\ + <KeyRelease>Left: NotifyScroll(FullLength) EndScroll() + ! ! Dialog boxes ! *serverDialog.dialog.label: VNC server: + *serverDialog.dialog.value: + *serverDialog.dialog.value.translations: #override\n\ - <Key>Return: ServerDialogDone() + <Key>Return: ServerDialogDone() + +*ycropDialog.dialog.label: Y Crop (max-height in pixels): + +*ycropDialog.dialog.value: + +*ycropDialog.dialog.value.translations: #override\n\ + <Key>Return: YCropDialogDone() + +*scbarDialog.dialog.label: Scroll Bars width: + +*scbarDialog.dialog.value: + +*scbarDialog.dialog.value.translations: #override\n\ + <Key>Return: ScbarDialogDone() + +*scaleDialog.dialog.label: Integer n for 1/n server scaling: + +*scaleDialog.dialog.value: + +*scaleDialog.dialog.value.translations: #override\n\ + <Key>Return: ScaleDialogDone() *passwordDialog.dialog.label: Password: + *passwordDialog.dialog.value: + *passwordDialog.dialog.value.AsciiSink.echo: False + *passwordDialog.dialog.value.translations: #override\n\ - <Key>Return: PasswordDialogDone() + <Key>Return: PasswordDialogDone() ! ! Popup window appearance ! -*popup.title: TightVNC popup +*popup.title: SSVNC popup + *popup*background: grey -*popup*font: -*-helvetica-bold-r-*-*-16-*-*-*-*-*-*-* -*popup.buttonForm.Command.borderWidth: 0 -*popup.buttonForm.Toggle.borderWidth: 0 + +*popup*font_old: -*-helvetica-bold-r-*-*-16-*-*-*-*-*-*-* + +*popup*font: -*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-* + +*popup.buttonForm*.Command.borderWidth: 0 + +*popup.buttonForm*.Toggle.borderWidth: 0 + +*scaleN.title: 1/n scale + +*scaleN*background: grey + +*scaleN*font: -*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-* + +*scaleN.buttonForm.Command.borderWidth: 0 + +*scaleN.buttonForm.Toggle.borderWidth: 0 + +*quality.title: quality + +*quality*background: grey + +*quality*font: -*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-* + +*quality.buttonForm.Command.borderWidth: 0 + +*quality.buttonForm.Toggle.borderWidth: 0 + +*compress.title: compress + +*compress*background: grey + +*compress*font: -*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-* + +*compress.buttonForm.Command.borderWidth: 0 + +*compress.buttonForm.Toggle.borderWidth: 0 + ! ! Translations on popup window - send key presses through ! *popup.translations: #override <Message>WM_PROTOCOLS: HidePopup() + *popup.buttonForm.translations: #override\n\ - <KeyPress>: SendRFBEvent() HidePopup() + <KeyPress>: SendRFBEvent() HidePopup() ! ! Popup buttons ! -*popupButtonCount: 8 +*popupButtonCount: 38 + +*popupButtonBreak: 19 *popup*button1.label: Dismiss popup + *popup*button1.translations: #override\n\ - <Btn1Down>,<Btn1Up>: HidePopup() + <Btn1Down>,<Btn1Up>: HidePopup() *popup*button2.label: Quit viewer + *popup*button2.translations: #override\n\ - <Btn1Down>,<Btn1Up>: Quit() + <Btn1Down>,<Btn1Up>: Quit() + +*popup*button3.label: Full screen (also F9) -*popup*button3.label: Full screen *popup*button3.type: toggle + *popup*button3.translations: #override\n\ - <Visible>: SetFullScreenState()\n\ - <Btn1Down>,<Btn1Up>: toggle() HidePopup() ToggleFullScreen() + <Visible>: SetFullScreenState()\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleFullScreen() HidePopup() *popup*button4.label: Clipboard: local -> remote + *popup*button4.translations: #override\n\ - <Btn1Down>,<Btn1Up>: SelectionToVNC(always) HidePopup() + <Btn1Down>,<Btn1Up>: SelectionToVNC(always) HidePopup() *popup*button5.label: Clipboard: local <- remote + *popup*button5.translations: #override\n\ - <Btn1Down>,<Btn1Up>: SelectionFromVNC(always) HidePopup() + <Btn1Down>,<Btn1Up>: SelectionFromVNC(always) HidePopup() *popup*button6.label: Request refresh + *popup*button6.translations: #override\n\ - <Btn1Down>,<Btn1Up>: SendRFBEvent(fbupdate) HidePopup() + <Btn1Down>,<Btn1Up>: SendRFBEvent(fbupdate) HidePopup() *popup*button7.label: Send ctrl-alt-del + *popup*button7.translations: #override\n\ - <Btn1Down>,<Btn1Up>: SendRFBEvent(keydown,Control_L)\ - SendRFBEvent(keydown,Alt_L)\ - SendRFBEvent(key,Delete)\ - SendRFBEvent(keyup,Alt_L)\ - SendRFBEvent(keyup,Control_L)\ - HidePopup() + <Btn1Down>,<Btn1Up>: SendRFBEvent(keydown,Control_L) SendRFBEvent(keydown,Alt_L) SendRFBEvent(key,Delete) SendRFBEvent(keyup,Alt_L) SendRFBEvent(keyup,Control_L) HidePopup() *popup*button8.label: Send F8 + *popup*button8.translations: #override\n\ - <Btn1Down>,<Btn1Up>: SendRFBEvent(key,F8) HidePopup() + <Btn1Down>,<Btn1Up>: SendRFBEvent(key,F8) HidePopup() + +*popup*button9.label: Send F9 + +*popup*button9.translations: #override\n\ + <Btn1Down>,<Btn1Up>: SendRFBEvent(key,F9) HidePopup() + +*popup*button10.label: ViewOnly + +*popup*button10.type: toggle + +*popup*button10.translations: #override\n\ + <Visible>: SetViewOnlyState()\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleViewOnly() HidePopup() + +*popup*button11.label: Disable Bell + +*popup*button11.type: toggle + +*popup*button11.translations: #override\n\ + <Visible>: SetBellState()\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleBell() HidePopup() + +*popup*button12.label: Cursor Shape + +*popup*button12.type: toggle + +*popup*button12.translations: #override\n\ + <Visible>: SetCursorShapeState()\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleCursorShape() HidePopup() + +*popup*button13.label: X11 Cursor + +*popup*button13.type: toggle + +*popup*button13.translations: #override\n\ + <Visible>: SetX11CursorState()\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleX11Cursor() HidePopup() + +*popup*button14.label: Cursor Alphablend + +*popup*button14.type: toggle + +*popup*button14.translations: #override\n\ + <Visible>: SetCursorAlphaState()\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleCursorAlpha() HidePopup() + +*popup*button15.label: Toggle Tight/ZRLE + +*popup*button15.type: toggle + +*popup*button15.translations: #override\n\ + <Visible>: SetZRLEState()\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleTightZRLE() HidePopup() + +*popup*button16.label: Toggle ZRLE/ZYWRLE + +*popup*button16.type: toggle + +*popup*button16.translations: #override\n\ + <Visible>: SetZYWRLEState()\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleZRLEZYWRLE() HidePopup() + +*popup*button17.label: Quality Level + +*popup*button17.translations: #override\n\ + <Btn1Down>,<Btn1Up>: HidePopup() ShowQuality() + +*popup*button18.label: Compress Level + +*popup*button18.translations: #override\n\ + <Btn1Down>,<Btn1Up>: HidePopup() ShowCompress() + +*popup*button19.label: Disable JPEG + +*popup*button19.type: toggle + +*popup*button19.translations: #override\n\ + <Visible>: SetNOJPEGState()\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleJPEG() HidePopup() + +*popup*button20.label: Full Color + +*popup*button20.type: toggle + +*popup*button20.translations: #override\n\ + <Visible>: SetFullColorState()\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleFullColor() HidePopup() + +*popup*button21.label: Grey Scale (16 & 8-bpp) + +*popup*button21.type: toggle + +*popup*button21.translations: #override\n\ + <Visible>: SetGreyScaleState()\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleGreyScale() HidePopup() + +*popup*button22.label: 16 bit color (BGR565) + +*popup*button22.type: toggle + +*popup*button22.translations: #override\n\ + <Visible>: Set16bppState()\n\ + <Btn1Down>,<Btn1Up>: toggle() Toggle16bpp() HidePopup() + +*popup*button23.label: 8 bit color (BGR233) + +*popup*button23.type: toggle + +*popup*button23.translations: #override\n\ + <Visible>: Set8bppState()\n\ + <Btn1Down>,<Btn1Up>: toggle() Toggle8bpp() HidePopup() + +*popup*button24.label: - 256 colors + +*popup*button24.type: toggle + +*popup*button24.translations: #override\n\ + <Visible>: Set256ColorsState()\n\ + <Btn1Down>,<Btn1Up>: toggle() Toggle256Colors() HidePopup() + +*popup*button25.label: - 64 colors + +*popup*button25.type: toggle + +*popup*button25.translations: #override\n\ + <Visible>: Set64ColorsState()\n\ + <Btn1Down>,<Btn1Up>: toggle() Toggle64Colors() HidePopup() + +*popup*button26.label: - 8 colors + +*popup*button26.type: toggle + +*popup*button26.translations: #override\n\ + <Visible>: Set8ColorsState()\n\ + <Btn1Down>,<Btn1Up>: toggle() Toggle8Colors() HidePopup() + +*popup*button27.label: Set Y Crop (y-max) + +*popup*button27.translations: #override\n\ + <Btn1Down>,<Btn1Up>: HidePopup() SetYCrop() + +*popup*button28.label: Set Scrollbar Width + +*popup*button28.translations: #override\n\ + <Btn1Down>,<Btn1Up>: HidePopup() SetScbar() + +*popup*button29.label: UltraVNC Extensions: + +*popup*button29.translations: #override\n\ + <Btn1Down>,<Btn1Up>: HidePopup() + +*popup*button30.label: - Set 1/n Server Scale + +*popup*button30.translations: #override\n\ + <Btn1Down>,<Btn1Up>: HidePopup() ShowScaleN() + +*popup*button31.label: - Text Chat + +*popup*button31.type: toggle + +*popup*button31.translations: #override\n\ + <Visible>: SetTextChatState()\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleTextChat() HidePopup() + +*popup*button32.label: - File Transfer + +*popup*button32.type: toggle + +*popup*button32.translations: #override\n\ + <Visible>: SetFileXferState()\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleFileXfer() HidePopup() + +*popup*button33.label: - Single Window + +*popup*button33.type: toggle + +*popup*button33.translations: #override\n\ + <Visible>: SetSingleWindowState()\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleSingleWindow() HidePopup() + +*popup*button34.label: - Disable Remote Input + +*popup*button34.type: toggle + +*popup*button34.translations: #override\n\ + <Visible>: SetServerInputState()\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleServerInput() HidePopup() + +*popup*button35.label: + +*popup*button36.label: + +*popup*button37.label: + +*popup*button38.label: + +*scaleN*button0.label: Dismiss + +*scaleN*button0.translations: #override\n\ + <Btn1Down>,<Btn1Up>: HideScaleN() + +*scaleN*button1.label: 1/1 + +*scaleN*button1.translations: #override\n\ + <Visible>: SetScaleNState(1)\n\ + <Btn1Down>,<Btn1Up>: SetScaleN(1) HideScaleN() + +*scaleN*button2.label: 1/2 + +*scaleN*button2.translations: #override\n\ + <Visible>: SetScaleNState(2)\n\ + <Btn1Down>,<Btn1Up>: SetScaleN(2) HideScaleN() + +*scaleN*button3.label: 1/3 + +*scaleN*button3.translations: #override\n\ + <Visible>: SetScaleNState(3)\n\ + <Btn1Down>,<Btn1Up>: SetScaleN(3) HideScaleN() + +*scaleN*button4.label: 1/4 + +*scaleN*button4.translations: #override\n\ + <Visible>: SetScaleNState(4)\n\ + <Btn1Down>,<Btn1Up>: SetScaleN(4) HideScaleN() + +*scaleN*button5.label: 1/5 + +*scaleN*button5.translations: #override\n\ + <Visible>: SetScaleNState(5)\n\ + <Btn1Down>,<Btn1Up>: SetScaleN(5) HideScaleN() + +*scaleN*button6.label: Other + +*scaleN*button6.translations: #override\n\ + <Visible>: SetScaleNState(6)\n\ + <Btn1Down>,<Btn1Up>: HideScaleN() DoServerScale() + +*quality*buttonD.label: Dismiss + +*quality*buttonD.translations: #override\n\ + <Btn1Down>,<Btn1Up>: HideQuality() + +*quality*button0.label: 0 + +*quality*button0.type: toggle + +*quality*button0.translations: #override\n\ + <Visible>: SetQualityState(0)\n\ + <Btn1Down>,<Btn1Up>: SetQuality(0) HideQuality() + +*quality*button1.label: 1 + +*quality*button1.type: toggle + +*quality*button1.translations: #override\n\ + <Visible>: SetQualityState(1)\n\ + <Btn1Down>,<Btn1Up>: SetQuality(1) HideQuality() + +*quality*button2.label: 2 + +*quality*button2.type: toggle + +*quality*button2.translations: #override\n\ + <Visible>: SetQualityState(2)\n\ + <Btn1Down>,<Btn1Up>: SetQuality(2) HideQuality() + +*quality*button3.label: 3 + +*quality*button3.type: toggle + +*quality*button3.translations: #override\n\ + <Visible>: SetQualityState(3)\n\ + <Btn1Down>,<Btn1Up>: SetQuality(3) HideQuality() + +*quality*button4.label: 4 + +*quality*button4.type: toggle + +*quality*button4.translations: #override\n\ + <Visible>: SetQualityState(4)\n\ + <Btn1Down>,<Btn1Up>: SetQuality(4) HideQuality() + +*quality*button5.label: 5 + +*quality*button5.type: toggle + +*quality*button5.translations: #override\n\ + <Visible>: SetQualityState(5)\n\ + <Btn1Down>,<Btn1Up>: SetQuality(5) HideQuality() + +*quality*button6.label: 6 + +*quality*button6.type: toggle + +*quality*button6.translations: #override\n\ + <Visible>: SetQualityState(6)\n\ + <Btn1Down>,<Btn1Up>: SetQuality(6) HideQuality() + +*quality*button7.label: 7 + +*quality*button7.type: toggle + +*quality*button7.translations: #override\n\ + <Visible>: SetQualityState(7)\n\ + <Btn1Down>,<Btn1Up>: SetQuality(7) HideQuality() + +*quality*button8.label: 8 + +*quality*button8.type: toggle + +*quality*button8.translations: #override\n\ + <Visible>: SetQualityState(8)\n\ + <Btn1Down>,<Btn1Up>: SetQuality(8) HideQuality() + +*quality*button9.label: 9 + +*quality*button9.type: toggle + +*quality*button9.translations: #override\n\ + <Visible>: SetQualityState(9)\n\ + <Btn1Down>,<Btn1Up>: SetQuality(9) HideQuality() + +*compress*buttonD.label: Dismiss + +*compress*buttonD.translations: #override\n\ + <Btn1Down>,<Btn1Up>: HideCompress() + +*compress*button0.label: 0 + +*compress*button0.translations: #override\n\ + <Visible>: SetCompressState(0)\n\ + <Btn1Down>,<Btn1Up>: SetCompress(0) HideCompress() + +*compress*button1.label: 1 + +*compress*button1.translations: #override\n\ + <Visible>: SetCompressState(1)\n\ + <Btn1Down>,<Btn1Up>: SetCompress(1) HideCompress() + +*compress*button2.label: 2 + +*compress*button2.translations: #override\n\ + <Visible>: SetCompressState(2)\n\ + <Btn1Down>,<Btn1Up>: SetCompress(2) HideCompress() + +*compress*button3.label: 3 + +*compress*button3.translations: #override\n\ + <Visible>: SetCompressState(3)\n\ + <Btn1Down>,<Btn1Up>: SetCompress(3) HideCompress() + +*compress*button4.label: 4 + +*compress*button4.translations: #override\n\ + <Visible>: SetCompressState(4)\n\ + <Btn1Down>,<Btn1Up>: SetCompress(4) HideCompress() + +*compress*button5.label: 5 + +*compress*button5.translations: #override\n\ + <Visible>: SetCompressState(5)\n\ + <Btn1Down>,<Btn1Up>: SetCompress(5) HideCompress() + +*compress*button6.label: 6 + +*compress*button6.translations: #override\n\ + <Visible>: SetCompressState(6)\n\ + <Btn1Down>,<Btn1Up>: SetCompress(6) HideCompress() + +*compress*button7.label: 7 + +*compress*button7.translations: #override\n\ + <Visible>: SetCompressState(7)\n\ + <Btn1Down>,<Btn1Up>: SetCompress(7) HideCompress() + +*compress*button8.label: 8 + +*compress*button8.translations: #override\n\ + <Visible>: SetCompressState(8)\n\ + <Btn1Down>,<Btn1Up>: SetCompress(8) HideCompress() + +*compress*button9.label: 9 + +*compress*button9.translations: #override\n\ + <Visible>: SetCompressState(9)\n\ + <Btn1Down>,<Btn1Up>: SetCompress(9) HideCompress() + diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/argsresources.c vnc_unixsrc/vncviewer/argsresources.c --- vnc_unixsrc.orig/vncviewer/argsresources.c 2007-02-04 17:10:31.000000000 -0500 +++ vnc_unixsrc/vncviewer/argsresources.c 2010-04-18 12:39:55.000000000 -0400 @@ -31,9 +31,9 @@ char *fallback_resources[] = { - "Vncviewer.title: TightVNC: %s", + "Ssvnc.title: SSVNC: %s - Press F8 for Menu", - "Vncviewer.translations:\ + "Ssvnc.translations:\ <Enter>: SelectionToVNC()\\n\ <Leave>: SelectionFromVNC()", @@ -45,8 +45,60 @@ "*viewport.useRight: True", "*viewport*Scrollbar*thumb: None", + "*viewport.horizontal.height: 6 ", + "*viewport.vertical.width: 6 ", + "ssvnc*viewport.horizontal.height: 6 ", + "ssvnc*viewport.vertical.width: 6 ", + + "*viewport.horizontal.translations: #override\\n\ + <KeyPress>Right: StartScroll(Forward)\\n\ + <KeyRelease>Right: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>Left: StartScroll(Backward)\\n\ + <KeyRelease>Left: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>Next: StartScroll(Forward)\\n\ + <KeyRelease>Next: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>Prior: StartScroll(Backward)\\n\ + <KeyRelease>Prior: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>z: StartScroll(Forward)\\n\ + <KeyRelease>z: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>a: StartScroll(Backward)\\n\ + <KeyRelease>a: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>f: StartScroll(Forward)\\n\ + <KeyRelease>f: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>b: StartScroll(Backward)\\n\ + <KeyRelease>b: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>Down: StartScroll(Forward)\\n\ + <KeyRelease>Down: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>Up: StartScroll(Backward)\\n\ + <KeyRelease>Up: NotifyScroll(FullLength) EndScroll()", + + "*viewport.vertical.translations: #override\\n\ + <KeyPress>Down: StartScroll(Forward)\\n\ + <KeyRelease>Down: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>Up: StartScroll(Backward)\\n\ + <KeyRelease>Up: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>Next: StartScroll(Forward)\\n\ + <KeyRelease>Next: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>Prior: StartScroll(Backward)\\n\ + <KeyRelease>Prior: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>z: StartScroll(Forward)\\n\ + <KeyRelease>z: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>a: StartScroll(Backward)\\n\ + <KeyRelease>a: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>f: StartScroll(Forward)\\n\ + <KeyRelease>f: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>b: StartScroll(Backward)\\n\ + <KeyRelease>b: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>Right: StartScroll(Forward)\\n\ + <KeyRelease>Right: NotifyScroll(FullLength) EndScroll()\\n\ + <KeyPress>Left: StartScroll(Backward)\\n\ + <KeyRelease>Left: NotifyScroll(FullLength) EndScroll()", + "*desktop.baseTranslations:\ - <Key>F8: ShowPopup()\\n\ + <KeyPress>F8: ShowPopup()\\n\ + <KeyRelease>F8: Noop()\\n\ + <KeyPress>F9: ToggleFullScreen()\\n\ + <KeyRelease>F9: Noop()\\n\ <ButtonPress>: SendRFBEvent()\\n\ <ButtonRelease>: SendRFBEvent()\\n\ <Motion>: SendRFBEvent()\\n\ @@ -55,26 +107,137 @@ "*serverDialog.dialog.label: VNC server:", "*serverDialog.dialog.value:", + "*serverDialog.dialog.value.width: 150", "*serverDialog.dialog.value.translations: #override\\n\ <Key>Return: ServerDialogDone()", - "*passwordDialog.dialog.label: Password:", + "*userDialog.dialog.label: SSVNC: Enter Username", + "*userDialog.dialog.value:", + "*userDialog.dialog.value.width: 150", + "*userDialog.dialog.value.translations: #override\\n\ + <Key>Return: UserDialogDone()", + + "*scaleDialog.dialog.label: Scale: Enter 'none' (same as '1' or '1.0'),\\na geometry WxH (e.g. 1280x1024), or\\na fraction (e.g. 0.75 or 3/4).\\nUse 'fit' for full screen size.\\nUse 'auto' to match window size.\\nCurrent value:", + "*scaleDialog.dialog.value:", + "*scaleDialog.dialog.value.translations: #override\\n\ + <KeyRelease>Return: ScaleDialogDone()", + + "*escapeDialog.dialog.label: Escape Keys: Enter a comma separated list of modifier keys to be the\\n" + "'escape sequence'. When these keys are held down, the next keystroke is\\n" + "interpreted locally to invoke a special action instead of being sent to\\n" + "the remote VNC server. In other words, a set of 'Hot Keys'.\\n" + "\\n" + "To enable or disable this, click on 'Escape Keys: Toggle' in the Popup.\\n" + "\\n" + "Here is the list of hot-key mappings to special actions:\\n" + "\\n" + " r: refresh desktop b: toggle bell c: toggle full-color\\n" + " f: file transfer x: x11cursor z: toggle Tight/ZRLE\\n" + " l: full screen g: graball e: escape keys dialog\\n" + " s: scale dialog +: scale up (=) -: scale down (_)\\n" + " t: text chat a: alphablend cursor\\n" + " V: toggle viewonly Q: quit viewer 1 2 3 4 5 6: UltraVNC scale 1/n\\n" + "\\n" + " Arrow keys: pan the viewport about 10% for each keypress.\\n" + " PageUp / PageDown: pan the viewport by a screenful vertically.\\n" + " Home / End: pan the viewport by a screenful horizontally.\\n" + " KeyPad Arrow keys: pan the viewport by 1 pixel for each keypress.\\n" + " Dragging the Mouse with Button1 pressed also pans the viewport.\\n" + " Clicking Mouse Button3 brings up the Popup Menu.\\n" + "\\n" + "The above mappings are *always* active in ViewOnly mode, unless you set the\\n" + "Escape Keys value to 'never'.\\n" + "\\n" + "x11vnc -appshare hot-keys: x11vnc has a simple application sharing mode\\n" + "that enables the viewer-side to move, resize, or raise the remote toplevel\\n" + "windows. To enable it, hold down Shift + the Escape Keys and press these:\\n" + "\\n" + " Arrow keys: move the remote window around in its desktop.\\n" + " PageUp/PageDn/Home/End: resize the remote window.\\n" + " +/- raise or lower the remote window.\\n" + " M or Button1 move win to local position; D or Button3: delete remote win.\\n" + "\\n" + "If the Escape Keys value below is set to 'default' then a fixed list of\\n" + "modifier keys is used. For Unix it is: Alt_L,Super_L and for MacOSX it is\\n" + "Control_L,Meta_L. Note: the Super_L key usually has a Windows(TM) Flag.\\n" + "Also note the _L and _R mean the key is on the LEFT or RIGHT side of keyboard.\\n" + "\\n" + "On Unix the default is Alt and Windows keys on Left side of keyboard.\\n" + "On MacOSX the default is Control and Command keys on Left side of keyboard.\\n" + "\\n" + "Example: Press and hold the Alt and Windows keys on the LEFT side of the\\n" + "keyboard and then press 'c' to toggle the full-color state. Or press 't'\\n" + "to toggle the ultravnc Text Chat window, etc.\\n" + "\\n" + "To use something besides the default, supply a comma separated list (or a\\n" + "single one) from: Shift_L Shift_R Control_L Control_R Alt_L Alt_R Meta_L\\n" + "Meta_R Super_L Super_R Hyper_L Hyper_R or Mode_switch.\\n" + "\\n" + "Current Escape Keys Value:", + "*escapeDialog.dialog.value:", + "*escapeDialog.dialog.value.width: 280", + "*escapeDialog.dialog.value.translations: #override\\n\ + <KeyRelease>Return: EscapeDialogDone()", + + "*ycropDialog.dialog.label: Y Crop (max-height in pixels):", + "*ycropDialog.dialog.value:", + "*ycropDialog.dialog.value.translations: #override\\n\ + <KeyRelease>Return: YCropDialogDone()", + + "*scbarDialog.dialog.label: Scroll Bars width:", + "*scbarDialog.dialog.value:", + "*scbarDialog.dialog.value.translations: #override\\n\ + <KeyRelease>Return: ScbarDialogDone()", + + "*scaleNDialog.dialog.label: Integer n for 1/n server scaling:", + "*scaleNDialog.dialog.value:", + "*scaleNDialog.dialog.value.translations: #override\\n\ + <KeyRelease>Return: ScaleNDialogDone()", + + "*passwordDialog.dialog.label: SSVNC: Enter Password", "*passwordDialog.dialog.value:", + "*passwordDialog.dialog.value.width: 150", "*passwordDialog.dialog.value.AsciiSink.echo: False", "*passwordDialog.dialog.value.translations: #override\\n\ <Key>Return: PasswordDialogDone()", - "*popup.title: TightVNC popup", + "*popup.title: SSVNC popup", "*popup*background: grey", - "*popup*font: -*-helvetica-bold-r-*-*-16-*-*-*-*-*-*-*", - "*popup.buttonForm.Command.borderWidth: 0", - "*popup.buttonForm.Toggle.borderWidth: 0", + "*popup*font_old: -*-helvetica-bold-r-*-*-16-*-*-*-*-*-*-*", + "*popup*font: -*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*", + "*popup.buttonForm*.Command.borderWidth: 0", + "*popup.buttonForm*.Toggle.borderWidth: 0", + + "*scaleN.title: 1/n scale", + "*scaleN*background: grey", + "*scaleN*font: -*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*", + "*scaleN.buttonForm.Command.borderWidth: 0", + "*scaleN.buttonForm.Toggle.borderWidth: 0", + + "*turboVNC.title: TurboVNC", + "*turboVNC*background: grey", + "*turboVNC*font: -*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*", + "*turboVNC.buttonForm.Command.borderWidth: 0", + "*turboVNC.buttonForm.Toggle.borderWidth: 0", + + "*quality.title: quality", + "*quality*background: grey", + "*quality*font: -*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*", + "*quality.buttonForm.Command.borderWidth: 0", + "*quality.buttonForm.Toggle.borderWidth: 0", + + "*compress.title: compress", + "*compress*background: grey", + "*compress*font: -*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*", + "*compress.buttonForm.Command.borderWidth: 0", + "*compress.buttonForm.Toggle.borderWidth: 0", "*popup.translations: #override <Message>WM_PROTOCOLS: HidePopup()", "*popup.buttonForm.translations: #override\\n\ <KeyPress>: SendRFBEvent() HidePopup()", - "*popupButtonCount: 8", + "*popupButtonCount: 44", + "*popupButtonBreak: 22", "*popup*button1.label: Dismiss popup", "*popup*button1.translations: #override\\n\ @@ -84,7 +247,7 @@ "*popup*button2.translations: #override\\n\ <Btn1Down>,<Btn1Up>: Quit()", - "*popup*button3.label: Full screen", + "*popup*button3.label: Full screen (also F9)", "*popup*button3.type: toggle", "*popup*button3.translations: #override\\n\ <Visible>: SetFullScreenState()\\n\ @@ -105,16 +268,426 @@ "*popup*button7.label: Send ctrl-alt-del", "*popup*button7.translations: #override\\n\ <Btn1Down>,<Btn1Up>: SendRFBEvent(keydown,Control_L)\ - SendRFBEvent(keydown,Alt_L)\ - SendRFBEvent(key,Delete)\ - SendRFBEvent(keyup,Alt_L)\ - SendRFBEvent(keyup,Control_L)\ - HidePopup()", + SendRFBEvent(keydown,Alt_L)\ + SendRFBEvent(key,Delete)\ + SendRFBEvent(keyup,Alt_L)\ + SendRFBEvent(keyup,Control_L)\ + HidePopup()", "*popup*button8.label: Send F8", "*popup*button8.translations: #override\\n\ <Btn1Down>,<Btn1Up>: SendRFBEvent(key,F8) HidePopup()", + "*popup*button9.label: Send F9", + "*popup*button9.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: SendRFBEvent(key,F9) HidePopup()", + + "*popup*button10.label: ViewOnly", + "*popup*button10.type: toggle", + "*popup*button10.translations: #override\\n\ + <Visible>: SetViewOnlyState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleViewOnly() HidePopup()", + + "*popup*button11.label: Disable Bell", + "*popup*button11.type: toggle", + "*popup*button11.translations: #override\\n\ + <Visible>: SetBellState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleBell() HidePopup()", + + "*popup*button12.label: Cursor Shape", + "*popup*button12.type: toggle", + "*popup*button12.translations: #override\\n\ + <Visible>: SetCursorShapeState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleCursorShape() HidePopup()", + + "*popup*button13.label: X11 Cursor", + "*popup*button13.type: toggle", + "*popup*button13.translations: #override\\n\ + <Visible>: SetX11CursorState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleX11Cursor() HidePopup()", + + "*popup*button14.label: Cursor Alphablend", + "*popup*button14.type: toggle", + "*popup*button14.translations: #override\\n\ + <Visible>: SetCursorAlphaState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleCursorAlpha() HidePopup()", + + "*popup*button15.label: Toggle Tight/Hextile", + "*popup*button15.type: toggle", + "*popup*button15.translations: #override\\n\ + <Visible>: SetHextileState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleTightHextile() HidePopup()", + + "*popup*button16.label: Toggle Tight/ZRLE", + "*popup*button16.type: toggle", + "*popup*button16.translations: #override\\n\ + <Visible>: SetZRLEState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleTightZRLE() HidePopup()", + + "*popup*button17.label: Toggle ZRLE/ZYWRLE", + "*popup*button17.type: toggle", + "*popup*button17.translations: #override\\n\ + <Visible>: SetZYWRLEState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleZRLEZYWRLE() HidePopup()", + + "*popup*button18.label: Quality Level", + "*popup*button18.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: HidePopup() ShowQuality()", + + "*popup*button19.label: Compress Level", + "*popup*button19.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: HidePopup() ShowCompress()", + + "*popup*button20.label: Disable JPEG", + "*popup*button20.type: toggle", + "*popup*button20.translations: #override\\n\ + <Visible>: SetNOJPEGState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleJPEG() HidePopup()", + + "*popup*button21.label: TurboVNC Settings", + "*popup*button21.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: HidePopup() ShowTurboVNC()", + + "*popup*button22.label: Pipeline Updates", + "*popup*button22.type: toggle", + "*popup*button22.translations: #override\\n\ + <Visible>: SetPipelineUpdates()\\n\ + <Btn1Down>,<Btn1Up>: toggle() TogglePipelineUpdates() HidePopup()", + + "*popup*button23.label: Full Color", + "*popup*button23.type: toggle", + "*popup*button23.translations: #override\\n\ + <Visible>: SetFullColorState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleFullColor() HidePopup()", + + "*popup*button24.label: Grey Scale (16 & 8-bpp)", + "*popup*button24.type: toggle", + "*popup*button24.translations: #override\\n\ + <Visible>: SetGreyScaleState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleGreyScale() HidePopup()", + + "*popup*button25.label: 16 bit color (BGR565)", + "*popup*button25.type: toggle", + "*popup*button25.translations: #override\\n\ + <Visible>: Set16bppState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() Toggle16bpp() HidePopup()", + + "*popup*button26.label: 8 bit color (BGR233)", + "*popup*button26.type: toggle", + "*popup*button26.translations: #override\\n\ + <Visible>: Set8bppState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() Toggle8bpp() HidePopup()", + + "*popup*button27.label: - 256 colors", + "*popup*button27.type: toggle", + "*popup*button27.translations: #override\\n\ + <Visible>: Set256ColorsState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() Toggle256Colors() HidePopup()", + + "*popup*button28.label: - 64 colors", + "*popup*button28.type: toggle", + "*popup*button28.translations: #override\\n\ + <Visible>: Set64ColorsState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() Toggle64Colors() HidePopup()", + + "*popup*button29.label: - 8 colors", + "*popup*button29.type: toggle", + "*popup*button29.translations: #override\\n\ + <Visible>: Set8ColorsState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() Toggle8Colors() HidePopup()", + + "*popup*button30.label: Scale Viewer", + "*popup*button30.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: HidePopup() SetScale()", + + "*popup*button31.label: Escape Keys: Toggle", + "*popup*button31.type: toggle", + "*popup*button31.translations: #override\\n\ + <Visible>: SetEscapeKeysState()\\n\ + <Btn1Down>, <Btn1Up>: toggle() ToggleEscapeActive() HidePopup()", + + "*popup*button32.label: Escape Keys: Help+Set", + "*popup*button32.translations: #override\\n\ + <Btn1Down>, <Btn1Up>: HidePopup() SetEscapeKeys()", + + "*popup*button33.label: Set Y Crop (y-max)", + "*popup*button33.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: HidePopup() SetYCrop()", + + "*popup*button34.label: Set Scrollbar Width", + "*popup*button34.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: HidePopup() SetScbar()", + + "*popup*button35.label: XGrabServer", + "*popup*button35.type: toggle", + "*popup*button35.translations: #override\\n\ + <Visible>: SetXGrabState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleXGrab() HidePopup()", + + "*popup*button36.label: UltraVNC Extensions:", + "*popup*button36.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: HidePopup()", + + "*popup*button37.label: - Set 1/n Server Scale", + "*popup*button37.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: HidePopup() ShowScaleN()", + + "*popup*button38.label: - Text Chat", + "*popup*button38.type: toggle", + "*popup*button38.translations: #override\\n\ + <Visible>: SetTextChatState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleTextChat() HidePopup()", + + "*popup*button39.label: - File Transfer", + "*popup*button39.type: toggle", + "*popup*button39.translations: #override\\n\ + <Visible>: SetFileXferState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleFileXfer() HidePopup()", + + "*popup*button40.label: - Single Window", + "*popup*button40.type: toggle", + "*popup*button40.translations: #override\\n\ + <Visible>: SetSingleWindowState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleSingleWindow() HidePopup()", + + "*popup*button41.label: - Disable Remote Input", + "*popup*button41.type: toggle", + "*popup*button41.translations: #override\\n\ + <Visible>: SetServerInputState()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleServerInput() HidePopup()", + + "*popup*button42.label: Send Clipboard not Primary", + "*popup*button42.type: toggle", + "*popup*button42.translations: #override\\n\ + <Visible>: SetSendClipboard()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleSendClipboard() HidePopup()", + + "*popup*button43.label: Send Selection Every time", + "*popup*button43.type: toggle", + "*popup*button43.translations: #override\\n\ + <Visible>: SetSendAlways()\\n\ + <Btn1Down>,<Btn1Up>: toggle() ToggleSendAlways() HidePopup()", + + "*popup*button44.label: ", + + "*turboVNC*button0.label: Dismiss", + "*turboVNC*button0.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: HideTurboVNC()", + + "*turboVNC*button1.label: High Quality (LAN)", + "*turboVNC*button1.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: SetTurboVNC(1)", + + "*turboVNC*button2.label: Medium Quality", + "*turboVNC*button2.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: SetTurboVNC(2)", + + "*turboVNC*button3.label: Low Quality (WAN)", + "*turboVNC*button3.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: SetTurboVNC(3)", + + "*turboVNC*button4.label: Lossless (Gigabit)", + "*turboVNC*button4.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: SetTurboVNC(4)", + + "*turboVNC*button5.label: Lossless Zlib (WAN)", + "*turboVNC*button5.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: SetTurboVNC(5)", + + "*turboVNC*button6.label: Subsampling:", + + "*turboVNC*button7.label: - None", + "*turboVNC*button7.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: SetTurboVNC(6)", + + "*turboVNC*button8.label: - 2X", + "*turboVNC*button8.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: SetTurboVNC(7)", + + "*turboVNC*button9.label: - 4X", + "*turboVNC*button9.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: SetTurboVNC(8)", + + "*turboVNC*button10.label: - Gray", + "*turboVNC*button10.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: SetTurboVNC(9)", + + "*turboVNC*button11.label: Lossless Refresh", + "*turboVNC*button11.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: SetTurboVNC(10)", + + "*turboVNC*button12.label: Lossy Refresh", + "*turboVNC*button12.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: SendRFBEvent(fbupdate)", + + "*turboVNC*buttonNone.label: Not Compiled with\\nTurboVNC Support.", + "*turboVNC*buttonNone.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: HideTurboVNC()", + + "*qualLabel.label: JPEG Image Quality:", + "*qualBar.length: 100", + "*qualBar.width: 130", + "*qualBar.orientation: horizontal", + "*qualBar.translations: #override\\n\ + <Btn1Down>: StartScroll(Continuous) MoveThumb() NotifyThumb()\\n\ + <Btn1Motion>: MoveThumb() NotifyThumb()\\n\ + <Btn3Down>: StartScroll(Continuous) MoveThumb() NotifyThumb()\\n\ + <Btn3Motion>: MoveThumb() NotifyThumb()", + + "*qualText.label: 000", + + "*scaleN*button0.label: Dismiss", + "*scaleN*button0.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: HideScaleN()", + + "*scaleN*button1.label: 1/1", + "*scaleN*button1.translations: #override\\n\ + <Visible>: SetScaleNState(1)\\n\ + <Btn1Down>,<Btn1Up>: SetScaleN(1) HideScaleN()", + + "*scaleN*button2.label: 1/2", + "*scaleN*button2.translations: #override\\n\ + <Visible>: SetScaleNState(2)\\n\ + <Btn1Down>,<Btn1Up>: SetScaleN(2) HideScaleN()", + + "*scaleN*button3.label: 1/3", + "*scaleN*button3.translations: #override\\n\ + <Visible>: SetScaleNState(3)\\n\ + <Btn1Down>,<Btn1Up>: SetScaleN(3) HideScaleN()", + + "*scaleN*button4.label: 1/4", + "*scaleN*button4.translations: #override\\n\ + <Visible>: SetScaleNState(4)\\n\ + <Btn1Down>,<Btn1Up>: SetScaleN(4) HideScaleN()", + + "*scaleN*button5.label: 1/5", + "*scaleN*button5.translations: #override\\n\ + <Visible>: SetScaleNState(5)\\n\ + <Btn1Down>,<Btn1Up>: SetScaleN(5) HideScaleN()", + + "*scaleN*button6.label: Other", + "*scaleN*button6.translations: #override\\n\ + <Visible>: SetScaleNState(6)\\n\ + <Btn1Down>,<Btn1Up>: HideScaleN() DoServerScale()", + + "*quality*buttonD.label: Dismiss", + "*quality*buttonD.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: HideQuality()", + + "*quality*button0.label: 0", + "*quality*button0.type: toggle", + "*quality*button0.translations: #override\\n\ + <Visible>: SetQualityState(0)\\n\ + <Btn1Down>,<Btn1Up>: SetQuality(0) HideQuality()", + + "*quality*button1.label: 1", + "*quality*button1.type: toggle", + "*quality*button1.translations: #override\\n\ + <Visible>: SetQualityState(1)\\n\ + <Btn1Down>,<Btn1Up>: SetQuality(1) HideQuality()", + + "*quality*button2.label: 2", + "*quality*button2.type: toggle", + "*quality*button2.translations: #override\\n\ + <Visible>: SetQualityState(2)\\n\ + <Btn1Down>,<Btn1Up>: SetQuality(2) HideQuality()", + + "*quality*button3.label: 3", + "*quality*button3.type: toggle", + "*quality*button3.translations: #override\\n\ + <Visible>: SetQualityState(3)\\n\ + <Btn1Down>,<Btn1Up>: SetQuality(3) HideQuality()", + + "*quality*button4.label: 4", + "*quality*button4.type: toggle", + "*quality*button4.translations: #override\\n\ + <Visible>: SetQualityState(4)\\n\ + <Btn1Down>,<Btn1Up>: SetQuality(4) HideQuality()", + + "*quality*button5.label: 5", + "*quality*button5.type: toggle", + "*quality*button5.translations: #override\\n\ + <Visible>: SetQualityState(5)\\n\ + <Btn1Down>,<Btn1Up>: SetQuality(5) HideQuality()", + + "*quality*button6.label: 6", + "*quality*button6.type: toggle", + "*quality*button6.translations: #override\\n\ + <Visible>: SetQualityState(6)\\n\ + <Btn1Down>,<Btn1Up>: SetQuality(6) HideQuality()", + + "*quality*button7.label: 7", + "*quality*button7.type: toggle", + "*quality*button7.translations: #override\\n\ + <Visible>: SetQualityState(7)\\n\ + <Btn1Down>,<Btn1Up>: SetQuality(7) HideQuality()", + + "*quality*button8.label: 8", + "*quality*button8.type: toggle", + "*quality*button8.translations: #override\\n\ + <Visible>: SetQualityState(8)\\n\ + <Btn1Down>,<Btn1Up>: SetQuality(8) HideQuality()", + + "*quality*button9.label: 9", + "*quality*button9.type: toggle", + "*quality*button9.translations: #override\\n\ + <Visible>: SetQualityState(9)\\n\ + <Btn1Down>,<Btn1Up>: SetQuality(9) HideQuality()", + + "*compress*buttonD.label: Dismiss", + "*compress*buttonD.translations: #override\\n\ + <Btn1Down>,<Btn1Up>: HideCompress()", + + "*compress*button0.label: 0", + "*compress*button0.translations: #override\\n\ + <Visible>: SetCompressState(0)\\n\ + <Btn1Down>,<Btn1Up>: SetCompress(0) HideCompress()", + + "*compress*button1.label: 1", + "*compress*button1.translations: #override\\n\ + <Visible>: SetCompressState(1)\\n\ + <Btn1Down>,<Btn1Up>: SetCompress(1) HideCompress()", + + "*compress*button2.label: 2", + "*compress*button2.translations: #override\\n\ + <Visible>: SetCompressState(2)\\n\ + <Btn1Down>,<Btn1Up>: SetCompress(2) HideCompress()", + + "*compress*button3.label: 3", + "*compress*button3.translations: #override\\n\ + <Visible>: SetCompressState(3)\\n\ + <Btn1Down>,<Btn1Up>: SetCompress(3) HideCompress()", + + "*compress*button4.label: 4", + "*compress*button4.translations: #override\\n\ + <Visible>: SetCompressState(4)\\n\ + <Btn1Down>,<Btn1Up>: SetCompress(4) HideCompress()", + + "*compress*button5.label: 5", + "*compress*button5.translations: #override\\n\ + <Visible>: SetCompressState(5)\\n\ + <Btn1Down>,<Btn1Up>: SetCompress(5) HideCompress()", + + "*compress*button6.label: 6", + "*compress*button6.translations: #override\\n\ + <Visible>: SetCompressState(6)\\n\ + <Btn1Down>,<Btn1Up>: SetCompress(6) HideCompress()", + + "*compress*button7.label: 7", + "*compress*button7.translations: #override\\n\ + <Visible>: SetCompressState(7)\\n\ + <Btn1Down>,<Btn1Up>: SetCompress(7) HideCompress()", + + "*compress*button8.label: 8", + "*compress*button8.translations: #override\\n\ + <Visible>: SetCompressState(8)\\n\ + <Btn1Down>,<Btn1Up>: SetCompress(8) HideCompress()", + + "*compress*button9.label: 9", + "*compress*button9.translations: #override\\n\ + <Visible>: SetCompressState(9)\\n\ + <Btn1Down>,<Btn1Up>: SetCompress(9) HideCompress()", + NULL }; @@ -124,7 +697,7 @@ * from a dialog box. */ -char vncServerHost[256]; +char vncServerHost[1024]; int vncServerPort = 0; @@ -135,6 +708,7 @@ */ AppData appData; +AppData appDataNew; static XtResource appDataResourceList[] = { {"shareDesktop", "ShareDesktop", XtRBool, sizeof(Bool), @@ -155,14 +729,44 @@ {"userLogin", "UserLogin", XtRString, sizeof(String), XtOffsetOf(AppData, userLogin), XtRImmediate, (XtPointer) 0}, + {"unixPW", "UnixPW", XtRString, sizeof(String), + XtOffsetOf(AppData, unixPW), XtRImmediate, (XtPointer) 0}, + + {"msLogon", "MSLogon", XtRString, sizeof(String), + XtOffsetOf(AppData, msLogon), XtRImmediate, (XtPointer) 0}, + + {"repeaterUltra", "RepeaterUltra", XtRString, sizeof(String), + XtOffsetOf(AppData, repeaterUltra), XtRImmediate, (XtPointer) 0}, + + {"ultraDSM", "UltraDSM", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, ultraDSM), XtRImmediate, (XtPointer) False}, + + {"acceptPopup", "AcceptPopup", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, acceptPopup), XtRImmediate, (XtPointer) False}, + + {"rfbVersion", "RfbVersion", XtRString, sizeof(String), + XtOffsetOf(AppData, rfbVersion), XtRImmediate, (XtPointer) 0}, + {"passwordDialog", "PasswordDialog", XtRBool, sizeof(Bool), XtOffsetOf(AppData, passwordDialog), XtRImmediate, (XtPointer) False}, {"encodings", "Encodings", XtRString, sizeof(String), XtOffsetOf(AppData, encodingsString), XtRImmediate, (XtPointer) 0}, - {"useBGR233", "UseBGR233", XtRBool, sizeof(Bool), - XtOffsetOf(AppData, useBGR233), XtRImmediate, (XtPointer) False}, + {"useBGR233", "UseBGR233", XtRInt, sizeof(int), + XtOffsetOf(AppData, useBGR233), XtRImmediate, (XtPointer) 0}, + + {"useBGR565", "UseBGR565", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, useBGR565), XtRImmediate, (XtPointer) False}, + + {"useGreyScale", "UseGreyScale", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, useGreyScale), XtRImmediate, (XtPointer) False}, + + {"yCrop", "yCrop", XtRInt, sizeof(int), + XtOffsetOf(AppData, yCrop), XtRImmediate, (XtPointer) 0}, + + {"sbWidth", "sbWidth", XtRInt, sizeof(int), + XtOffsetOf(AppData, sbWidth), XtRImmediate, (XtPointer) 2}, {"nColours", "NColours", XtRInt, sizeof(int), XtOffsetOf(AppData, nColours), XtRImmediate, (XtPointer) 256}, @@ -179,9 +783,12 @@ {"requestedDepth", "RequestedDepth", XtRInt, sizeof(int), XtOffsetOf(AppData, requestedDepth), XtRImmediate, (XtPointer) 0}, - {"useSharedMemory", "UseSharedMemory", XtRBool, sizeof(Bool), + {"useShm", "UseShm", XtRBool, sizeof(Bool), XtOffsetOf(AppData, useShm), XtRImmediate, (XtPointer) True}, + {"termChat", "TermChat", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, termChat), XtRImmediate, (XtPointer) False}, + {"wmDecorationWidth", "WmDecorationWidth", XtRInt, sizeof(int), XtOffsetOf(AppData, wmDecorationWidth), XtRImmediate, (XtPointer) 4}, @@ -191,6 +798,9 @@ {"popupButtonCount", "PopupButtonCount", XtRInt, sizeof(int), XtOffsetOf(AppData, popupButtonCount), XtRImmediate, (XtPointer) 0}, + {"popupButtonBreak", "PopupButtonBreak", XtRInt, sizeof(int), + XtOffsetOf(AppData, popupButtonBreak), XtRImmediate, (XtPointer) 0}, + {"debug", "Debug", XtRBool, sizeof(Bool), XtOffsetOf(AppData, debug), XtRImmediate, (XtPointer) False}, @@ -206,11 +816,13 @@ {"bumpScrollPixels", "BumpScrollPixels", XtRInt, sizeof(int), XtOffsetOf(AppData, bumpScrollPixels), XtRImmediate, (XtPointer) 20}, + /* hardwired compress -1 vs . 7 */ {"compressLevel", "CompressionLevel", XtRInt, sizeof(int), XtOffsetOf(AppData, compressLevel), XtRImmediate, (XtPointer) -1}, + /* hardwired quality was 6 */ {"qualityLevel", "QualityLevel", XtRInt, sizeof(int), - XtOffsetOf(AppData, qualityLevel), XtRImmediate, (XtPointer) 6}, + XtOffsetOf(AppData, qualityLevel), XtRImmediate, (XtPointer) -1}, {"enableJPEG", "EnableJPEG", XtRBool, sizeof(Bool), XtOffsetOf(AppData, enableJPEG), XtRImmediate, (XtPointer) True}, @@ -218,14 +830,97 @@ {"useRemoteCursor", "UseRemoteCursor", XtRBool, sizeof(Bool), XtOffsetOf(AppData, useRemoteCursor), XtRImmediate, (XtPointer) True}, + {"useCursorAlpha", "UseCursorAlpha", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, useCursorAlpha), XtRImmediate, (XtPointer) False}, + + {"useRawLocal", "UseRawLocal", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, useRawLocal), XtRImmediate, (XtPointer) False}, + + {"notty", "NoTty", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, notty), XtRImmediate, (XtPointer) False}, + {"useX11Cursor", "UseX11Cursor", XtRBool, sizeof(Bool), XtOffsetOf(AppData, useX11Cursor), XtRImmediate, (XtPointer) False}, + {"useBell", "UseBell", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, useBell), XtRImmediate, (XtPointer) True}, + {"grabKeyboard", "GrabKeyboard", XtRBool, sizeof(Bool), - XtOffsetOf(AppData, grabKeyboard), XtRImmediate, (XtPointer) False}, + XtOffsetOf(AppData, grabKeyboard), XtRImmediate, (XtPointer) True}, {"autoPass", "AutoPass", XtRBool, sizeof(Bool), - XtOffsetOf(AppData, autoPass), XtRImmediate, (XtPointer) False} + XtOffsetOf(AppData, autoPass), XtRImmediate, (XtPointer) False}, + + {"grabAll", "GrabAll", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, grabAll), XtRImmediate, (XtPointer) False}, + + {"useXserverBackingStore", "UseXserverBackingStore", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, useXserverBackingStore), XtRImmediate, (XtPointer) False}, + + {"overrideRedir", "OverrideRedir", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, overrideRedir), XtRImmediate, (XtPointer) True}, + + {"serverInput", "ServerInput", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, serverInput), XtRImmediate, (XtPointer) True}, + + {"singleWindow", "SingleWindow", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, singleWindow), XtRImmediate, (XtPointer) False}, + + {"serverScale", "ServerScale", XtRInt, sizeof(int), + XtOffsetOf(AppData, serverScale), XtRImmediate, (XtPointer) 1}, + + {"chatActive", "ChatActive", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, chatActive), XtRImmediate, (XtPointer) False}, + + {"chatOnly", "ChatOnly", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, chatOnly), XtRImmediate, (XtPointer) False}, + + {"fileActive", "FileActive", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, fileActive), XtRImmediate, (XtPointer) False}, + + {"popupFix", "PopupFix", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, popupFix), XtRImmediate, (XtPointer) False}, + + {"scale", "Scale", XtRString, sizeof(String), + XtOffsetOf(AppData, scale), XtRImmediate, (XtPointer) 0}, + + {"pipelineUpdates", "PipelineUpdates", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, pipelineUpdates), XtRImmediate, (XtPointer) +#ifdef TURBOVNC + True}, +#else +#if 0 + False}, +#else + True}, +#endif +#endif + + {"noipv4", "noipv4", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, noipv4), XtRImmediate, (XtPointer) False}, + + {"noipv6", "noipv6", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, noipv6), XtRImmediate, (XtPointer) False}, + + {"sendClipboard", "SendClipboard", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, sendClipboard), XtRImmediate, (XtPointer) False}, + + {"sendAlways", "SendAlways", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, sendAlways), XtRImmediate, (XtPointer) False}, + + {"recvText", "RecvText", XtRString, sizeof(String), + XtOffsetOf(AppData, recvText), XtRImmediate, (XtPointer) 0}, + + {"appShare", "AppShare", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, appShare), XtRImmediate, (XtPointer) False}, + + {"escapeKeys", "EscapeKeys", XtRString, sizeof(String), + XtOffsetOf(AppData, escapeKeys), XtRImmediate, (XtPointer) 0}, + + {"escapeActive", "EscapeActive", XtRBool, sizeof(Bool), + XtOffsetOf(AppData, escapeActive), XtRImmediate, (XtPointer) False} + + /* check commas */ }; @@ -242,8 +937,29 @@ {"-noraiseonbeep", "*raiseOnBeep", XrmoptionNoArg, "False"}, {"-passwd", "*passwordFile", XrmoptionSepArg, 0}, {"-user", "*userLogin", XrmoptionSepArg, 0}, + {"-unixpw", "*unixPW", XrmoptionSepArg, 0}, + {"-mslogon", "*msLogon", XrmoptionSepArg, 0}, + {"-repeater", "*repeaterUltra", XrmoptionSepArg, 0}, + {"-ultradsm", "*ultraDSM", XrmoptionNoArg, "True"}, + {"-acceptpopup", "*acceptPopup", XrmoptionNoArg, "True"}, + {"-acceptpopupsc", "*acceptPopup", XrmoptionNoArg, "True"}, + {"-rfbversion", "*rfbVersion", XrmoptionSepArg, 0}, {"-encodings", "*encodings", XrmoptionSepArg, 0}, - {"-bgr233", "*useBGR233", XrmoptionNoArg, "True"}, + {"-bgr233", "*useBGR233", XrmoptionNoArg, "256"}, + {"-use64", "*useBGR233", XrmoptionNoArg, "64"}, + {"-bgr222", "*useBGR233", XrmoptionNoArg, "64"}, + {"-use8", "*useBGR233", XrmoptionNoArg, "8"}, + {"-bgr111", "*useBGR233", XrmoptionNoArg, "8"}, + {"-16bpp", "*useBGR565", XrmoptionNoArg, "True"}, + {"-bgr565", "*useBGR565", XrmoptionNoArg, "True"}, + {"-grey", "*useGreyScale", XrmoptionNoArg, "True"}, + {"-gray", "*useGreyScale", XrmoptionNoArg, "True"}, + {"-sbwidth", "*sbwidth", XrmoptionSepArg, 0}, + {"-env", "*envDummy", XrmoptionSepArg, 0}, + {"-ycrop", "*yCrop", XrmoptionSepArg, 0}, + {"-rawlocal", "*useRawLocal", XrmoptionNoArg, "True"}, + {"-notty", "*notty", XrmoptionNoArg, "True"}, + {"-alpha", "*useCursorAlpha", XrmoptionNoArg, "True"}, {"-owncmap", "*forceOwnCmap", XrmoptionNoArg, "True"}, {"-truecolor", "*forceTrueColour", XrmoptionNoArg, "True"}, {"-truecolour", "*forceTrueColour", XrmoptionNoArg, "True"}, @@ -253,8 +969,30 @@ {"-nojpeg", "*enableJPEG", XrmoptionNoArg, "False"}, {"-nocursorshape", "*useRemoteCursor", XrmoptionNoArg, "False"}, {"-x11cursor", "*useX11Cursor", XrmoptionNoArg, "True"}, - {"-autopass", "*autoPass", XrmoptionNoArg, "True"} - + {"-nobell", "*useBell", XrmoptionNoArg, "False"}, + {"-autopass", "*autoPass", XrmoptionNoArg, "True"}, + {"-graball", "*grabAll", XrmoptionNoArg, "True"}, + {"-grabkbd", "*grabKeyboard", XrmoptionNoArg, "True"}, + {"-nograbkbd", "*grabKeyboard", XrmoptionNoArg, "False"}, + {"-grabkeyboard", "*grabKeyboard", XrmoptionNoArg, "True"}, + {"-nograbkeyboard","*grabKeyboard", XrmoptionNoArg, "False"}, + {"-nooverride", "*overrideRedir", XrmoptionNoArg, "False"}, + {"-bs", "*useXserverBackingStore", XrmoptionNoArg, "True"}, + {"-nobs", "*useXserverBackingStore", XrmoptionNoArg, "False"}, + {"-popupfix", "*popupFix", XrmoptionNoArg, "True"}, + {"-noshm", "*useShm", XrmoptionNoArg, "False"}, + {"-termchat", "*termChat", XrmoptionNoArg, "True"}, + {"-chatonly", "*chatOnly", XrmoptionNoArg, "True"}, + {"-scale", "*scale", XrmoptionSepArg, 0}, + {"-appshare", "*appShare", XrmoptionNoArg, "True"}, + {"-escape", "*escapeKeys", XrmoptionSepArg, 0}, + {"-sendclipboard", "*sendClipboard", XrmoptionNoArg, "True"}, + {"-sendalways", "*sendAlways", XrmoptionNoArg, "True"}, + {"-recvtext", "*recvText", XrmoptionSepArg, 0}, + {"-pipeline", "*pipelineUpdates", XrmoptionNoArg, "True"}, + {"-nopipeline", "*pipelineUpdates", XrmoptionNoArg, "False"}, + {"-noipv4", "*noipv4", XrmoptionNoArg, "True"}, + {"-noipv6", "*noipv6", XrmoptionNoArg, "True"} }; int numCmdLineOptions = XtNumber(cmdLineOptions); @@ -267,16 +1005,100 @@ static XtActionsRec actions[] = { {"SendRFBEvent", SendRFBEvent}, {"ShowPopup", ShowPopup}, + {"Noop", Noop}, {"HidePopup", HidePopup}, + {"HideScaleN", HideScaleN}, + {"HideTurboVNC", HideTurboVNC}, + {"HideQuality", HideQuality}, + {"HideCompress", HideCompress}, {"ToggleFullScreen", ToggleFullScreen}, + {"JumpLeft", JumpLeft}, + {"JumpRight", JumpRight}, + {"JumpUp", JumpUp}, + {"JumpDown", JumpDown}, {"SetFullScreenState", SetFullScreenState}, {"SelectionFromVNC", SelectionFromVNC}, {"SelectionToVNC", SelectionToVNC}, {"ServerDialogDone", ServerDialogDone}, + {"UserDialogDone", UserDialogDone}, + {"YCropDialogDone", YCropDialogDone}, + {"ScbarDialogDone", ScbarDialogDone}, + {"ScaleNDialogDone", ScaleNDialogDone}, + {"ScaleDialogDone", ScaleDialogDone}, {"PasswordDialogDone", PasswordDialogDone}, {"Pause", Pause}, {"RunCommand", RunCommand}, {"Quit", Quit}, + {"HideChat", HideChat}, + {"Toggle8bpp", Toggle8bpp}, + {"Toggle16bpp", Toggle16bpp}, + {"ToggleFullColor", ToggleFullColor}, + {"Toggle256Colors", Toggle256Colors}, + {"Toggle64Colors", Toggle64Colors}, + {"Toggle8Colors", Toggle8Colors}, + {"ToggleGreyScale", ToggleGreyScale}, + {"ToggleTightZRLE", ToggleTightZRLE}, + {"ToggleTightHextile", ToggleTightHextile}, + {"ToggleZRLEZYWRLE", ToggleZRLEZYWRLE}, + {"ToggleViewOnly", ToggleViewOnly}, + {"ToggleJPEG", ToggleJPEG}, + {"ToggleCursorShape", ToggleCursorShape}, + {"ToggleCursorAlpha", ToggleCursorAlpha}, + {"ToggleX11Cursor", ToggleX11Cursor}, + {"ToggleBell", ToggleBell}, + {"ToggleRawLocal", ToggleRawLocal}, + {"ToggleServerInput", ToggleServerInput}, + {"TogglePipelineUpdates", TogglePipelineUpdates}, + {"ToggleSendClipboard", ToggleSendClipboard}, + {"ToggleSendAlways", ToggleSendAlways}, + {"ToggleSingleWindow", ToggleSingleWindow}, + {"ToggleTextChat", ToggleTextChat}, + {"ToggleFileXfer", ToggleFileXfer}, + {"ToggleXGrab", ToggleXGrab}, + {"DoServerScale", DoServerScale}, + {"SetScale", SetScale}, + {"SetYCrop", SetYCrop}, + {"SetScbar", SetScbar}, + {"ShowScaleN", ShowScaleN}, + {"ShowTurboVNC", ShowTurboVNC}, + {"ShowQuality", ShowQuality}, + {"ShowCompress", ShowCompress}, + {"SetScaleN", SetScaleN}, + {"SetTurboVNC", SetTurboVNC}, + {"SetQuality", SetQuality}, + {"SetCompress", SetCompress}, + {"Set8bppState", Set8bppState}, + {"Set16bppState", Set16bppState}, + {"SetFullColorState", SetFullColorState}, + {"Set256ColorsState", Set256ColorsState}, + {"Set64ColorsState", Set64ColorsState}, + {"Set8ColorsState", Set8ColorsState}, + {"SetGreyScaleState", SetGreyScaleState}, + {"SetZRLEState", SetZRLEState}, + {"SetHextileState", SetHextileState}, + {"SetZYWRLEState", SetZYWRLEState}, + {"SetNOJPEGState", SetNOJPEGState}, + {"SetScaleNState", SetScaleNState}, + {"SetQualityState", SetQualityState}, + {"SetCompressState", SetCompressState}, + {"SetViewOnlyState", SetViewOnlyState}, + {"SetCursorShapeState", SetCursorShapeState}, + {"SetCursorAlphaState", SetCursorAlphaState}, + {"SetX11CursorState", SetX11CursorState}, + {"SetBellState", SetBellState}, + {"SetRawLocalState", SetRawLocalState}, + {"SetServerInputState", SetServerInputState}, + {"SetPipelineUpdates", SetPipelineUpdates}, + {"SetSendClipboard", SetSendClipboard}, + {"SetSendAlways", SetSendAlways}, + {"SetSingleWindowState", SetSingleWindowState}, + {"SetTextChatState", SetTextChatState}, + {"SetFileXferState", SetFileXferState}, + {"SetXGrabState", SetXGrabState}, + {"SetEscapeKeysState", SetEscapeKeysState}, + {"ToggleEscapeActive", ToggleEscapeActive}, + {"EscapeDialogDone", EscapeDialogDone}, + {"SetEscapeKeys", SetEscapeKeys} }; @@ -302,11 +1124,14 @@ void usage(void) { - fprintf(stderr, - "TightVNC viewer version 1.3dev7\n" + fprintf(stdout, + "SSVNC Viewer (based on TightVNC viewer version 1.3.9)\n" "\n" "Usage: %s [<OPTIONS>] [<HOST>][:<DISPLAY#>]\n" " %s [<OPTIONS>] [<HOST>][::<PORT#>]\n" + " %s [<OPTIONS>] exec=[CMD ARGS...]\n" + " %s [<OPTIONS>] fd=n\n" + " %s [<OPTIONS>] /path/to/unix/socket\n" " %s [<OPTIONS>] -listen [<DISPLAY#>]\n" " %s -help\n" "\n" @@ -319,7 +1144,7 @@ " -noraiseonbeep\n" " -passwd <PASSWD-FILENAME> (standard VNC authentication)\n" " -user <USERNAME> (Unix login authentication)\n" - " -encodings <ENCODING-LIST> (e.g. \"tight copyrect\")\n" + " -encodings <ENCODING-LIST> (e.g. \"tight,copyrect\")\n" " -bgr233\n" " -owncmap\n" " -truecolour\n" @@ -332,10 +1157,390 @@ " -autopass\n" "\n" "Option names may be abbreviated, e.g. -bgr instead of -bgr233.\n" - "See the manual page for more information." - "\n", programName, programName, programName, programName); + "See the manual page for more information.\n" + "\n" + "\n" + "Enhanced TightVNC viewer (SSVNC) options:\n" + "\n" + " URL http://www.karlrunge.com/x11vnc/ssvnc.html\n" + "\n" + " Note: ZRLE and ZYWRLE encodings are now supported.\n" + "\n" + " Note: F9 is shortcut to Toggle FullScreen mode.\n" + "\n" + " Note: In -listen mode set the env var. SSVNC_MULTIPLE_LISTEN=1\n" + " to allow more than one incoming VNC server at a time.\n" + " This is the same as -multilisten described below. Set\n" + " SSVNC_MULTIPLE_LISTEN=MAX:n to allow no more than \"n\"\n" + " simultaneous reverse connections.\n" + "\n" + " Note: If the host:port is specified as \"exec=command args...\"\n" + " then instead of making a TCP/IP socket connection to the\n" + " remote VNC server, \"command args...\" is executed and the\n" + " viewer is attached to its stdio. This enables tunnelling\n" + " established via an external command, e.g. an stunnel(8)\n" + " that does not involve a listening socket. This mode does\n" + " not work for -listen reverse connections.\n" + "\n" + " If the host:port is specified as \"fd=n\" then it is assumed\n" + " n is an already opened file descriptor to the socket. (i.e\n" + " the parent did fork+exec)\n" + "\n" + " If the host:port contains a '/' it is interpreted as a\n" + " unix-domain socket (AF_LOCAL insead of AF_INET)\n" + "\n" + " -multilisten As in -listen (reverse connection listening) except\n" + " allow more than one incoming VNC server to be connected\n" + " at a time. The default for -listen of only one at a\n" + " time tries to play it safe by not allowing anyone on\n" + " the network to put (many) desktops on your screen over\n" + " a long window of time. Use -multilisten for no limit.\n" + "\n" + " -acceptpopup In -listen (reverse connection listening) mode when\n" + " a reverse VNC connection comes in show a popup asking\n" + " whether to Accept or Reject the connection. The IP\n" + " address of the connecting host is shown. Same as\n" + " setting the env. var. SSVNC_ACCEPT_POPUP=1.\n" + "\n" + " -acceptpopupsc As in -acceptpopup except assume UltraVNC Single\n" + " Click (SC) server. Retrieve User and ComputerName\n" + " info from UltraVNC Server and display in the Popup.\n" + "\n" + " -use64 In -bgr233 mode, use 64 colors instead of 256.\n" + " -bgr222 Same as -use64.\n" + "\n" + " -use8 In -bgr233 mode, use 8 colors instead of 256.\n" + " -bgr111 Same as -use8.\n" + "\n" + " -16bpp If the vnc viewer X display is depth 24 at 32bpp\n" + " request a 16bpp format from the VNC server to cut\n" + " network traffic by up to 2X, then tranlate the\n" + " pixels to 32bpp locally.\n" + " -bgr565 Same as -16bpp.\n" + "\n" + " -grey Use a grey scale for the 16- and 8-bpp modes.\n" + "\n" + " -alpha Use alphablending transparency for local cursors\n" + " requires: x11vnc server, both client and server\n" + " must be 32bpp and same endianness.\n" + "\n" + " -scale str Scale the desktop locally. The string \"str\" can\n" + " a floating point ratio, e.g. \"0.9\", or a fraction,\n" + " e.g. \"3/4\", or WxH, e.g. 1280x1024. Use \"fit\"\n" + " to fit in the current screen size. Use \"auto\" to\n" + " fit in the window size. \"str\" can also be set by\n" + " the env. var. SSVNC_SCALE.\n" + "\n" + " If you observe mouse trail painting errors, enable\n" + " X11 Cursor mode (either via Popup or -x11cursor.)\n" + "\n" + " Note that scaling is done in software and so can be\n" + " slow and requires more memory. Some speedup Tips:\n" + "\n" + " ZRLE is faster than Tight in this mode. When\n" + " scaling is first detected, the encoding will\n" + " be automatically switched to ZRLE. Use the\n" + " Popup menu if you want to go back to Tight.\n" + " Set SSVNC_PRESERVE_ENCODING=1 to disable this.\n" + "\n" + " Use a solid background on the remote side.\n" + " (e.g. manually or via x11vnc -solid ...)\n" + "\n" + " If the remote server is x11vnc, try client\n" + " side caching: x11vnc -ncache 10 ...\n" + "\n" + " -ycrop n Only show the top n rows of the framebuffer. For\n" + " use with x11vnc -ncache client caching option\n" + " to help \"hide\" the pixel cache region.\n" + " Use a negative value (e.g. -1) for autodetection.\n" + " Autodetection will always take place if the remote\n" + " fb height is more than 2 times the width.\n" + "\n" + " -sbwidth n Scrollbar width for x11vnc -ncache mode (-ycrop),\n" + " default is very narrow: 2 pixels, it is narrow to\n" + " avoid distraction in -ycrop mode.\n" + "\n" + " -nobell Disable bell.\n" + "\n" + " -rawlocal Prefer raw encoding for localhost, default is\n" + " no, i.e. assumes you have a SSH tunnel instead.\n" + "\n" + " -notty Try to avoid using the terminal for interactive\n" + " responses: use windows for messages and prompting\n" + " instead. Messages will also be printed to terminal.\n" + "\n" + " -sendclipboard Send the X CLIPBOARD selection (i.e. Ctrl+C,\n" + " Ctrl+V) instead of the X PRIMARY selection (mouse\n" + " select and middle button paste.)\n" + "\n" + " -sendalways Whenever the mouse enters the VNC viewer main\n" + " window, send the selection to the VNC server even if\n" + " it has not changed. This is like the Xt resource\n" + " translation SelectionToVNC(always)\n" + "\n" + " -recvtext str When cut text is received from the VNC server,\n" + " ssvncviewer will set both the X PRIMARY and the\n" + " X CLIPBOARD local selections. To control which\n" + " is set, specify 'str' as 'primary', 'clipboard',\n" + " or 'both' (the default.)\n" + "\n" + " -graball Grab the entire X server when in fullscreen mode,\n" + " needed by some old window managers like fvwm2.\n" + "\n" + " -popupfix Warp the popup back to the pointer position,\n" + " needed by some old window managers like fvwm2.\n" + " -sendclipboard Send the X CLIPBOARD selection (i.e. Ctrl+C,\n" + " Ctrl+V) instead of the X PRIMARY selection (mouse\n" + " select and middle button paste.)\n" + "\n" + " -sendalways Whenever the mouse enters the VNC viewer main\n" + " window, send the selection to the VNC server even if\n" + " it has not changed. This is like the Xt resource\n" + " translation SelectionToVNC(always)\n" + "\n" + " -recvtext str When cut text is received from the VNC server,\n" + " ssvncviewer will set both the X PRIMARY and the\n" + " X CLIPBOARD local selections. To control which\n" + " is set, specify 'str' as 'primary', 'clipboard',\n" + " or 'both' (the default.)\n" + "\n" + " -graball Grab the entire X server when in fullscreen mode,\n" + " needed by some old window managers like fvwm2.\n" + "\n" + " -popupfix Warp the popup back to the pointer position,\n" + " needed by some old window managers like fvwm2.\n" + "\n" + " -grabkbd Grab the X keyboard when in fullscreen mode,\n" + " needed by some window managers. Same as -grabkeyboard.\n" + " -grabkbd is the default, use -nograbkbd to disable.\n" + "\n" + " -bs, -nobs Whether or not to use X server Backingstore for the\n" + " main viewer window. The default is to not, mainly\n" + " because most Linux, etc, systems X servers disable\n" + " *all* Backingstore by default. To re-enable it put\n" + "\n" + " Option \"Backingstore\"\n" + "\n" + " in the Device section of /etc/X11/xorg.conf.\n" + " In -bs mode with no X server backingstore, whenever an\n" + " area of the screen is re-exposed it must go out to the\n" + " VNC server to retrieve the pixels. This is too slow.\n" + "\n" + " In -nobs mode, memory is allocated by the viewer to\n" + " provide its own backing of the main viewer window. This\n" + " actually makes some activities faster (changes in large\n" + " regions) but can appear to \"flash\" too much.\n" + "\n" + " -noshm Disable use of MIT shared memory extension (not recommended)\n" + "\n" + " -termchat Do the UltraVNC chat in the terminal vncviewer is in\n" + " instead of in an independent window.\n" + "\n" + " -unixpw str Useful for logging into x11vnc in -unixpw mode. \"str\" is a\n" + " string that allows many ways to enter the Unix Username\n" + " and Unix Password. These characters: username, newline,\n" + " password, newline are sent to the VNC server after any VNC\n" + " authentication has taken place. Under x11vnc they are\n" + " used for the -unixpw login. Other VNC servers could do\n" + " something similar.\n" + "\n" + " You can also indicate \"str\" via the environment\n" + " variable SSVNC_UNIXPW.\n" + "\n" + " Note that the Escape key is actually sent first to tell\n" + " x11vnc to not echo the Unix Username back to the VNC\n" + " viewer. Set SSVNC_UNIXPW_NOESC=1 to override this.\n" + "\n" + " If str is \".\", then you are prompted at the command line\n" + " for the username and password in the normal way. If str is\n" + " \"-\" the stdin is read via getpass(3) for username@password.\n" + " Otherwise if str is a file, it is opened and the first line\n" + " read is taken as the Unix username and the 2nd as the\n" + " password. If str prefixed by \"rm:\" the file is removed\n" + " after reading. Otherwise, if str has a \"@\" character,\n" + " it is taken as username@password. Otherwise, the program\n" + " exits with an error. Got all that?\n" + "\n" + " -repeater str This is for use with UltraVNC repeater proxy described\n" + " here: http://www.uvnc.com/addons/repeater.html. The \"str\"\n" + " is the ID string to be sent to the repeater. E.g. ID:1234\n" + " It can also be the hostname and port or display of the VNC\n" + " server, e.g. 12.34.56.78:0 or snoopy.com:1. Note that when\n" + " using -repeater, the host:dpy on the cmdline is the repeater\n" + " server, NOT the VNC server. The repeater will connect you.\n" + "\n" + " Example: vncviewer ... -repeater ID:3333 repeat.host:5900\n" + " Example: vncviewer ... -repeater vhost:0 repeat.host:5900\n" + "\n" + " Use, e.g., '-repeater SCIII=ID:3210' if the repeater is a\n" + " Single Click III (SSL) repeater (repeater_SSL.exe) and you\n" + " are passing the SSL part of the connection through stunnel,\n" + " socat, etc. This way the magic UltraVNC string 'testB'\n" + " needed to work with the repeater is sent to it.\n" + "\n" + " -rfbversion str Set the advertised RFB version. E.g.: -rfbversion 3.6\n" + " For some servers, e.g. UltraVNC this needs to be done.\n" + "\n" + " -ultradsm UltraVNC has symmetric private key encryption DSM plugins:\n" + " http://www.uvnc.com/features/encryption.html. It is assumed\n" + " you are using a unix program (e.g. our ultravnc_dsm_helper)\n" + " to encrypt and decrypt the UltraVNC DSM stream. IN ADDITION\n" + " TO THAT supply -ultradsm to tell THIS viewer to modify the\n" + " RFB data sent so as to work with the UltraVNC Server. For\n" + " some reason, each RFB msg type must be sent twice under DSM.\n" + "\n" + " -mslogon user Use Windows MS Logon to an UltraVNC server. Supply the\n" + " username or \"1\" to be prompted. The default is to\n" + " autodetect the UltraVNC MS Logon server and prompt for\n" + " the username and password.\n" + "\n" + " IMPORTANT NOTE: The UltraVNC MS-Logon Diffie-Hellman\n" + " exchange is very weak and can be brute forced to recover\n" + " your username and password in a few seconds of CPU time.\n" + " To be safe, be sure to use an additional encrypted tunnel\n" + " (e.g. SSL or SSH) for the entire VNC session.\n" + "\n" + " -chatonly Try to be a client that only does UltraVNC text chat. This\n" + " mode is used by x11vnc to present a chat window on the\n" + " physical X11 console (i.e. chat with the person at the\n" + " display).\n" + "\n" + " -env VAR=VALUE To save writing a shell script to set environment variables,\n" + " specify as many as you need on the command line. For\n" + " example, -env SSVNC_MULTIPLE_LISTEN=MAX:5 -env EDITOR=vi\n" + "\n" + " -noipv6 Disable all IPv6 sockets. Same as VNCVIEWER_NO_IPV6=1.\n" + "\n" + " -noipv4 Disable all IPv4 sockets. Same as VNCVIEWER_NO_IPV4=1.\n" + "\n" + " -printres Print out the Ssvnc X resources (appdefaults) and then exit\n" + " You can save them to a file and customize them (e.g. the\n" + " keybindings and Popup menu) Then point to the file via\n" + " XENVIRONMENT or XAPPLRESDIR.\n" + "\n" + " -pipeline Like TurboVNC, request the next framebuffer update as soon\n" + " as possible instead of waiting until the end of the current\n" + " framebuffer update coming in. Helps 'pipeline' the updates.\n" + " This is currently the default, use -nopipeline to disable.\n" + "\n" + " -appshare Enable features for use with x11vnc's -appshare mode where\n" + " instead of sharing the full desktop only the application's\n" + " windows are shared. Viewer multilisten mode is used to\n" + " create the multiple windows: -multilisten is implied.\n" + " See 'x11vnc -appshare -help' more information on the mode.\n" + "\n" + " Features enabled in the viewer under -appshare are:\n" + " Minimum extra text in the title, auto -ycrop is disabled,\n" + " x11vnc -remote_prefix X11VNC_APPSHARE_CMD: message channel,\n" + " x11vnc initial window position hints. See also Escape Keys\n" + " below for additional key and mouse bindings.\n" + "\n" + " -escape str This sets the 'Escape Keys' modifier sequence and enables\n" + " escape keys mode. When the modifier keys escape sequence\n" + " is held down, the next keystroke is interpreted locally\n" + " to perform a special action instead of being sent to the\n" + " remote VNC server.\n" + "\n" + " Use '-escape default' for the default modifier sequence.\n" + " (Unix: Alt_L,Super_L and MacOSX: Control_L,Meta_L)\n" + "\n" + " Here are the 'Escape Keys: Help+Set' instructions from the Popup Menu:\n" + "\n" + " Escape Keys: Enter a comma separated list of modifier keys to be the\n" + " 'escape sequence'. When these keys are held down, the next keystroke is\n" + " interpreted locally to invoke a special action instead of being sent to\n" + " the remote VNC server. In other words, a set of 'Hot Keys'.\n" + " \n" + " To enable or disable this, click on 'Escape Keys: Toggle' in the Popup.\n" + " \n" + " Here is the list of hot-key mappings to special actions:\n" + " \n" + " r: refresh desktop b: toggle bell c: toggle full-color\n" + " f: file transfer x: x11cursor z: toggle Tight/ZRLE\n" + " l: full screen g: graball e: escape keys dialog\n" + " s: scale dialog +: scale up (=) -: scale down (_)\n" + " t: text chat a: alphablend cursor\n" + " V: toggle viewonly Q: quit viewer 1 2 3 4 5 6: UltraVNC scale 1/n\n" + " \n" + " Arrow keys: pan the viewport about 10%% for each keypress.\n" + " PageUp / PageDown: pan the viewport by a screenful vertically.\n" + " Home / End: pan the viewport by a screenful horizontally.\n" + " KeyPad Arrow keys: pan the viewport by 1 pixel for each keypress.\n" + " Dragging the Mouse with Button1 pressed also pans the viewport.\n" + " Clicking Mouse Button3 brings up the Popup Menu.\n" + " \n" + " The above mappings are *always* active in ViewOnly mode, unless you set the\n" + " Escape Keys value to 'never'.\n" + " \n" + " If the Escape Keys value below is set to 'default' then a default list of\n" + " of modifier keys is used. For Unix it is: Alt_L,Super_L and for MacOSX it\n" + " is Control_L,Meta_L. Note: the Super_L key usually has a Windows(TM) Flag\n" + " on it. Also note the _L and _R mean the key is on the LEFT or RIGHT side\n" + " of the keyboard.\n" + " \n" + " On Unix the default is Alt and Windows keys on Left side of keyboard.\n" + " On MacOSX the default is Control and Command keys on Left side of keyboard.\n" + " \n" + " Example: Press and hold the Alt and Windows keys on the LEFT side of the\n" + " keyboard and then press 'c' to toggle the full-color state. Or press 't'\n" + " to toggle the ultravnc Text Chat window, etc.\n" + " \n" + " To use something besides the default, supply a comma separated list (or a\n" + " single one) from: Shift_L Shift_R Control_L Control_R Alt_L Alt_R Meta_L\n" + " Meta_R Super_L Super_R Hyper_L Hyper_R or Mode_switch.\n" + "\n" + "\n" + " New Popup actions:\n" + "\n" + " ViewOnly: ~ -viewonly\n" + " Disable Bell: ~ -nobell\n" + " Cursor Shape: ~ -nocursorshape\n" + " X11 Cursor: ~ -x11cursor\n" + " Cursor Alphablend: ~ -alpha\n" + " Toggle Tight/Hextile: ~ -encodings hextile...\n" + " Toggle Tight/ZRLE: ~ -encodings zrle...\n" + " Toggle ZRLE/ZYWRLE: ~ -encodings zywrle...\n" + " Quality Level ~ -quality (both Tight and ZYWRLE)\n" + " Compress Level ~ -compresslevel\n" + " Disable JPEG: ~ -nojpeg (Tight)\n" + " Pipeline Updates ~ -pipeline\n" + "\n" + " Full Color as many colors as local screen allows.\n" + " Grey scale (16 & 8-bpp) ~ -grey, for low colors 16/8bpp modes only.\n" + " 16 bit color (BGR565) ~ -16bpp / -bgr565\n" + " 8 bit color (BGR233) ~ -bgr233\n" + " 256 colors ~ -bgr233 default # of colors.\n" + " 64 colors ~ -bgr222 / -use64\n" + " 8 colors ~ -bgr111 / -use8\n" + " Scale Viewer ~ -scale\n" + " Escape Keys: Toggle ~ -escape\n" + " Escape Keys: Help+Set ~ -escape\n" + " Set Y Crop (y-max) ~ -ycrop\n" + " Set Scrollbar Width ~ -sbwidth\n" + " XGrabServer ~ -graball\n" + "\n" + " UltraVNC Extensions:\n" + "\n" + " Set 1/n Server Scale Ultravnc ext. Scale desktop by 1/n.\n" + " Text Chat Ultravnc ext. Do Text Chat.\n" + " File Transfer Ultravnc ext. File xfer via Java helper.\n" + " Single Window Ultravnc ext. Grab and view a single window.\n" + " (select then click on the window you want).\n" + " Disable Remote Input Ultravnc ext. Try to prevent input and\n" + " viewing of monitor at physical display.\n" + "\n" + " Note: the Ultravnc extensions only apply to servers that support\n" + " them. x11vnc/libvncserver supports some of them.\n" + "\n" + " Send Clipboard not Primary ~ -sendclipboard\n" + " Send Selection Every time ~ -sendalways\n" + "\n" + "\n", programName, programName, programName, programName, programName, programName, programName); exit(1); } +#if 0 + " -nooverride Do not apply OverrideRedirect in fullscreen mode.\n" +#endif /* @@ -343,77 +1548,247 @@ * not already processed by XtVaAppInitialize(). It sets vncServerHost and * vncServerPort and all the fields in appData. */ +extern int saw_appshare; void GetArgsAndResources(int argc, char **argv) { - int i; - char *vncServerName, *colonPos; - int len, portOffset; + char *vncServerName = NULL, *colonPos, *bracketPos; + int len, portOffset; + int disp; /* Turn app resource specs into our appData structure for the rest of the program to use */ - XtGetApplicationResources(toplevel, &appData, appDataResourceList, - XtNumber(appDataResourceList), 0, 0); + XtGetApplicationResources(toplevel, &appData, appDataResourceList, + XtNumber(appDataResourceList), 0, 0); + + /* + * we allow setting of some by env, to avoid clash with other + * viewer's cmdlines (e.g. change viewer in SSVNC). + */ + if (getenv("VNCVIEWER_ALPHABLEND")) { + appData.useCursorAlpha = True; + } + if (getenv("VNCVIEWER_POPUP_FIX")) { + if (getenv("NOPOPUPFIX")) { + ; + } else if (!strcmp(getenv("VNCVIEWER_POPUP_FIX"), "0")) { + ; + } else { + appData.popupFix = True; + } + } + if (getenv("VNCVIEWER_GRAB_SERVER")) { + appData.grabAll = True; + } + if (getenv("VNCVIEWER_YCROP")) { + int n = atoi(getenv("VNCVIEWER_YCROP")); + if (n != 0) { + appData.yCrop = n; + } + } + if (getenv("VNCVIEWER_RFBVERSION") && strcmp(getenv("VNCVIEWER_RFBVERSION"), "")) { + appData.rfbVersion = strdup(getenv("VNCVIEWER_RFBVERSION")); + } + if (getenv("VNCVIEWER_ENCODINGS") && strcmp(getenv("VNCVIEWER_ENCODINGS"), "")) { + appData.encodingsString = strdup(getenv("VNCVIEWER_ENCODINGS")); + } + if (getenv("VNCVIEWER_NOBELL")) { + appData.useBell = False; + } + if (getenv("VNCVIEWER_X11CURSOR")) { + appData.useX11Cursor = True; + } + if (getenv("VNCVIEWER_RAWLOCAL")) { + appData.useRawLocal = True; + } + if (getenv("VNCVIEWER_NOTTY") || getenv("SSVNC_VNCVIEWER_NOTTY")) { + appData.notty = True; + } + if (getenv("VNCVIEWER_SBWIDTH")) { + int n = atoi(getenv("VNCVIEWER_SBWIDTH")); + if (n != 0) { + appData.sbWidth = n; + } + } + if (getenv("VNCVIEWER_ULTRADSM")) { + appData.ultraDSM = True; + } + if (getenv("SSVNC_ULTRA_DSM") && strcmp(getenv("SSVNC_ULTRA_DSM"), "")) { + appData.ultraDSM = True; + } + if (getenv("SSVNC_NO_ULTRA_DSM")) { + appData.ultraDSM = False; + } + if (getenv("SSVNC_SCALE") && strcmp(getenv("SSVNC_SCALE"), "")) { + if (appData.scale == NULL) { + appData.scale = strdup(getenv("SSVNC_SCALE")); + } + } + if (getenv("VNCVIEWER_ESCAPE") && strcmp(getenv("VNCVIEWER_ESCAPE"), "")) { + if (appData.escapeKeys == NULL) { + appData.escapeKeys = strdup(getenv("VNCVIEWER_ESCAPE")); + } + } + if (saw_appshare) { + appData.appShare = True; + } + if (appData.appShare && appData.escapeKeys == NULL) { + appData.escapeKeys = strdup("default"); + } + if (appData.escapeKeys != NULL) { + appData.escapeActive = True; + } + if (getenv("VNCVIEWER_SEND_CLIPBOARD")) { + appData.sendClipboard = True; + } + if (getenv("VNCVIEWER_SEND_ALWAYS")) { + appData.sendAlways = True; + } + if (getenv("VNCVIEWER_RECV_TEXT")) { + char *s = getenv("VNCVIEWER_RECV_TEXT"); + if (!strcasecmp(s, "clipboard")) { + appData.recvText = strdup("clipboard"); + } else if (!strcasecmp(s, "primary")) { + appData.recvText = strdup("primary"); + } else if (!strcasecmp(s, "both")) { + appData.recvText = strdup("both"); + } + } + if (getenv("VNCVIEWER_PIPELINE_UPDATES")) { + appData.pipelineUpdates = True; + } else if (getenv("VNCVIEWER_NO_PIPELINE_UPDATES")) { + appData.pipelineUpdates = False; + } + + if (getenv("VNCVIEWER_NO_IPV4")) { + appData.noipv4 = True; + } + if (getenv("VNCVIEWER_NO_IPV6")) { + appData.noipv6 = True; + } + + if (appData.useBGR233 && appData.useBGR565) { + appData.useBGR233 = 0; + } + + if (getenv("SSVNC_ULTRA_FTP_JAR") == NULL && programName != NULL) { + int len = strlen(programName) + 200; + char *q, *jar = (char *) malloc(len); + + sprintf(jar, "%s", programName); + q = strrchr(jar, '/'); + if (q) { + struct stat sb; + *(q+1) = '\0'; + strcat(jar, "../lib/ssvnc/util/ultraftp.jar"); + if (stat(jar, &sb) == 0) { + char *put = (char *) malloc(len); + sprintf(put, "SSVNC_ULTRA_FTP_JAR=%s", jar); + fprintf(stderr, "Setting: %s\n\n", put); + putenv(put); + } else { + sprintf(jar, "%s", programName); + q = strrchr(jar, '/'); + *(q+1) = '\0'; + strcat(jar, "util/ultraftp.jar"); + if (stat(jar, &sb) == 0) { + char *put = (char *) malloc(len); + sprintf(put, "SSVNC_ULTRA_FTP_JAR=%s", jar); + fprintf(stderr, "Setting: %s\n\n", put); + putenv(put); + } + } + } + free(jar); + } + /* Add our actions to the actions table so they can be used in widget resource specs */ - XtAppAddActions(appContext, actions, XtNumber(actions)); + XtAppAddActions(appContext, actions, XtNumber(actions)); /* Check any remaining command-line arguments. If -listen was specified there should be none. Otherwise the only argument should be the VNC server name. If not given then pop up a dialog box and wait for the server name to be entered. */ - if (listenSpecified) { - if (argc != 1) { - fprintf(stderr,"\n%s -listen: invalid command line argument: %s\n", - programName, argv[1]); - usage(); - } - return; - } - - if (argc == 1) { - vncServerName = DoServerDialog(); - appData.passwordDialog = True; - } else if (argc != 2) { - usage(); - } else { - vncServerName = argv[1]; - - if (!isatty(0)) - appData.passwordDialog = True; - if (vncServerName[0] == '-') - usage(); - } - - if (strlen(vncServerName) > 255) { - fprintf(stderr,"VNC server name too long\n"); - exit(1); - } - - colonPos = strchr(vncServerName, ':'); - if (colonPos == NULL) { - /* No colon -- use default port number */ - strcpy(vncServerHost, vncServerName); - vncServerPort = SERVER_PORT_OFFSET; - } else { - memcpy(vncServerHost, vncServerName, colonPos - vncServerName); - vncServerHost[colonPos - vncServerName] = '\0'; - len = strlen(colonPos + 1); - portOffset = SERVER_PORT_OFFSET; - if (colonPos[1] == ':') { - /* Two colons -- interpret as a port number */ - colonPos++; - len--; - portOffset = 0; - } - if (!len || strspn(colonPos + 1, "0123456789") != len) { - usage(); - } - vncServerPort = atoi(colonPos + 1) + portOffset; - } + if (listenSpecified) { + if (argc != 1) { + fprintf(stderr,"\n%s -listen: invalid command line argument: %s\n", + programName, argv[1]); + usage(); + } + return; + } + + if (argc == 1) { + vncServerName = DoServerDialog(); + if (!use_tty()) { + appData.passwordDialog = True; + } + } else if (argc != 2) { + usage(); + } else { + vncServerName = argv[1]; + + if (!use_tty()) { + appData.passwordDialog = True; + } + if (vncServerName[0] == '-') { + usage(); + } + } + + if (strlen(vncServerName) > 255) { + fprintf(stderr,"VNC server name too long\n"); + exit(1); + } + + colonPos = strrchr(vncServerName, ':'); + bracketPos = strrchr(vncServerName, ']'); + if (strstr(vncServerName, "exec=") == vncServerName) { + /* special exec-external-command case */ + strcpy(vncServerHost, vncServerName); + vncServerPort = SERVER_PORT_OFFSET; + } else if (strstr(vncServerName, "fd=") == vncServerName) { + /* special exec-external-command case */ + strcpy(vncServerHost, vncServerName); + vncServerPort = SERVER_PORT_OFFSET; + } else if (colonPos == NULL) { + /* No colon -- use default port number */ + strcpy(vncServerHost, vncServerName); + vncServerPort = SERVER_PORT_OFFSET; + } else if (bracketPos != NULL && colonPos < bracketPos) { + strcpy(vncServerHost, vncServerName); + vncServerPort = SERVER_PORT_OFFSET; + } else { + if (colonPos > vncServerName && *(colonPos - 1) == ':') { + colonPos--; + } + memcpy(vncServerHost, vncServerName, colonPos - vncServerName); + vncServerHost[colonPos - vncServerName] = '\0'; + len = strlen(colonPos + 1); + portOffset = SERVER_PORT_OFFSET; + if (colonPos[1] == ':') { + /* Two colons -- interpret as a port number */ + colonPos++; + len--; + portOffset = 0; + } + if (!len || strspn(colonPos + 1, "0123456789") != (size_t) len) { + usage(); + } +#if 0 + vncServerPort = atoi(colonPos + 1) + portOffset; +#else + disp = atoi(colonPos + 1); + if (portOffset != 0 && disp >= 100) { + portOffset = 0; + } + vncServerPort = disp + portOffset; +#endif + } } diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/colour.c vnc_unixsrc/vncviewer/colour.c --- vnc_unixsrc.orig/vncviewer/colour.c 2002-04-30 09:07:31.000000000 -0400 +++ vnc_unixsrc/vncviewer/colour.c 2010-02-25 22:02:19.000000000 -0500 @@ -31,9 +31,12 @@ #define BGR233_SIZE 256 unsigned long BGR233ToPixel[BGR233_SIZE]; +#define BGR565_SIZE 65536 +unsigned long BGR565ToPixel[BGR565_SIZE]; + Colormap cmap; Visual *vis; -unsigned int visdepth, visbpp; +unsigned int visdepth, visbpp, isLSB; Bool allocColorFailed = False; static int nBGR233ColoursAllocated; @@ -45,6 +48,8 @@ static void AllocateExactBGR233Colours(); static Bool AllocateBGR233Colour(int r, int g, int b); +static void SetupBGR565Map(unsigned long red_mask, unsigned long green_mask, unsigned long blue_mask); + /* * SetVisualAndCmap() deals with the wonderful world of X "visuals" (which are @@ -97,6 +102,44 @@ visbpp = GetBPPForDepth(visdepth); cmap = DefaultColormap(dpy,DefaultScreen(dpy)); + if (ImageByteOrder(dpy) == LSBFirst) { + isLSB = 1; + } else { + isLSB = 0; + } + if (visbpp == 24) { + if (!appData.useBGR233) { + fprintf(stderr, "Warning: for 24bpp enabling -bgr565 -- Don't use FullColor!\n"); + appData.useBGR565 = True; + } else { + fprintf(stderr, "Warning: for 24bpp using -bgr233 -- Don't use FullColor!\n"); + } + } + + if (appData.useBGR565) { + if (visdepth < 24 || visbpp < 24 || vis->class != TrueColor) { + fprintf(stderr, "disabling -16bpp BGR565 on non-depth 24 machine\n"); + appData.useBGR565 = False; + } else { + myFormat.bitsPerPixel = 16; + myFormat.depth = 16; + myFormat.trueColour = 1; + myFormat.bigEndian = 0; + myFormat.redMax = 31; + myFormat.greenMax = 63; + myFormat.blueMax = 31; + myFormat.redShift = 11; + myFormat.greenShift = 5; + myFormat.blueShift = 0; + + fprintf(stderr, "Using default colormap and translating from BGR565 (65536 colors). Pixel format:\n"); + PrintPixelFormat(&myFormat); + + SetupBGR565Map(vis->red_mask, vis->green_mask, vis->blue_mask); + return; + } + } + if (!appData.useBGR233 && (vis->class == TrueColor)) { myFormat.bitsPerPixel = visbpp; @@ -116,21 +159,42 @@ return; } - appData.useBGR233 = True; + if (appData.useBGR233 == 0) { + appData.useBGR233 = 256; + } myFormat.bitsPerPixel = 8; myFormat.depth = 8; myFormat.trueColour = 1; myFormat.bigEndian = 0; - myFormat.redMax = 7; + myFormat.redMax = 7; myFormat.greenMax = 7; - myFormat.blueMax = 3; - myFormat.redShift = 0; + myFormat.blueMax = 3; + myFormat.redShift = 0; myFormat.greenShift = 3; - myFormat.blueShift = 6; + myFormat.blueShift = 6; + + if (appData.useBGR233 == 64) { + /* BGR222 */ + myFormat.redMax = 3; + myFormat.greenMax = 3; + myFormat.blueMax = 3; + myFormat.redShift = 0; + myFormat.greenShift = 2; + myFormat.blueShift = 4; + } + if (appData.useBGR233 == 8) { + /* BGR111 */ + myFormat.redMax = 2; + myFormat.greenMax = 2; + myFormat.blueMax = 2; + myFormat.redShift = 0; + myFormat.greenShift = 1; + myFormat.blueShift = 2; + } fprintf(stderr, - "Using default colormap and translating from BGR233. Pixel format:\n"); + "Using default colormap and translating from BGR233 (%d colors). Pixel format:\n", appData.useBGR233); PrintPixelFormat(&myFormat); SetupBGR233Map(); @@ -282,8 +346,12 @@ XFree(format); if (bpp != 1 && bpp != 8 && bpp != 16 && bpp != 32) { - fprintf(stderr,"Can't cope with %d bits-per-pixel. Sorry.\n", bpp); - exit(1); + if (bpp == 24) { + fprintf(stderr,"Warning: 24 bits-per-pixel may have problems...\n"); + } else { + fprintf(stderr,"Can't cope with %d bits-per-pixel. Sorry.\n", bpp); + exit(1); + } } return bpp; @@ -374,7 +442,7 @@ if (!exactBGR233[i] && XAllocColor(dpy, cmap, &cmapEntry[i])) { - if (cmapEntry[i].pixel == i) { + if ((long) cmapEntry[i].pixel == i) { shared[i] = True; /* probably shared */ @@ -394,16 +462,43 @@ for (r = 0; r < 8; r++) { for (g = 0; g < 8; g++) { for (b = 0; b < 4; b++) { - if (BGR233ToPixel[(b<<6) | (g<<3) | r] == INVALID_PIXEL) { + int bs = 6, gs = 3, rs = 0; + int bm = 3, gm = 7, rm = 7; + if (appData.useBGR233 == 64) { + bs = 4; gs = 2; rs = 0; + bm = 3; gm = 3; rm = 3; + } + if (appData.useBGR233 == 8) { + bs = 2; gs = 1; rs = 0; + bm = 1; gm = 1; rm = 1; + } + if ((b > bm || g > gm || r > rm)) { + continue; + } + if (BGR233ToPixel[(b<<bs) | (g<<gs) | (r<<rs)] == INVALID_PIXEL) { unsigned long minDistance = ULONG_MAX; for (i = 0; i < cmapSize; i++) { if (exactBGR233[i] || shared[i]) { - unsigned long distance - = (abs(cmapEntry[i].red - r * 65535 / 7) - + abs(cmapEntry[i].green - g * 65535 / 7) - + abs(cmapEntry[i].blue - b * 65535 / 3)); + unsigned long distance; + int r1, g1, b1; + if (appData.useGreyScale) { + int ave; + ave = (r + g + 2*b)/3; + r1 = ave; + g1 = ave; + b1 = ave/2; + } else { + r1 = r; + g1 = g; + b1 = b; + } + distance + = ( abs(cmapEntry[i].red - r1 * 65535 / rm) + + abs(cmapEntry[i].green - g1 * 65535 / gm) + + abs(cmapEntry[i].blue - b1 * 65535 / bm)); + if (distance < minDistance) { minDistance = distance; @@ -412,7 +507,7 @@ } } - BGR233ToPixel[(b<<6) | (g<<3) | r] = nearestPixel; + BGR233ToPixel[(b<<bs) | (g<<gs) | (r<<rs)] = nearestPixel; if (shared[nearestPixel] && !usedAsNearest[nearestPixel]) nSharedUsed++; usedAsNearest[nearestPixel] = True; @@ -433,6 +528,59 @@ } } +static void +SetupBGR565Map(unsigned long red_mask, unsigned long green_mask, unsigned long blue_mask) +{ + int r, g, b; + int r2, g2, b2; + long idx; + int cnt = 0; + unsigned long pixel = 0; + + for (r = 0; r < 32; r++) { + for (g = 0; g < 64; g++) { + for (b = 0; b < 32; b++) { + int bs = 0, gs = 5, rs = 11; + int bm = 31, gm = 63, rm = 31; + if ((b > bm || g > gm || r > rm)) { + continue; + } + r2 = (255 * r) / rm; + g2 = (255 * g) / gm; + b2 = (255 * b) / bm; + + pixel = (r2 << 16) | (g2 << 8) | (b2 << 0); + if (appData.useGreyScale) { + int ave; + int r1, g1, b1; + ave = (2*r + g + 2*b)/3; + r1 = ave/2; + g1 = ave; + b1 = ave/2; + + r2 = (255 * r1) / rm; + g2 = (255 * g1) / gm; + b2 = (255 * b1) / bm; + + pixel = (r2 << 16) | (g2 << 8) | (b2 << 0); + } + + if (red_mask == 0xff) { + idx = (r<<bs) | (g<<gs) | (b<<rs); + } else { + idx = (b<<bs) | (g<<gs) | (r<<rs); + } + if (0) fprintf(stderr, "cnt: %5d idx: %lu pixel: 0x%08x\n", cnt, idx, (unsigned int) pixel); + BGR565ToPixel[idx] = pixel; + cnt++; + } + } + } + green_mask = 0; + blue_mask = 0; +} + + /* * AllocateExactBGR233Colours() attempts to allocate each of the colours in the @@ -484,8 +632,13 @@ ri = rn; for (gi = 0; gi < gn; gi++) { for (bi = 0; bi < bn; bi++) { - if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi])) - return; + if (appData.useBGR233 == 64 && (bv[bi] > 3 || gv[gi] > 3 || rv[ri] > 3)) { + nBGR233ColoursAllocated++; + } else if (appData.useBGR233 == 8 && (bv[bi] > 1 || gv[gi] > 1 || rv[ri] > 1)) { + nBGR233ColoursAllocated++; + } else if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi])) { + return; + } } } rn++; @@ -496,8 +649,13 @@ gi = gn; for (ri = 0; ri < rn; ri++) { for (bi = 0; bi < bn; bi++) { - if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi])) - return; + if (appData.useBGR233 == 64 && (bv[bi] > 3 || gv[gi] > 3 || rv[ri] > 3)) { + nBGR233ColoursAllocated++; + } else if (appData.useBGR233 == 8 && (bv[bi] > 1 || gv[gi] > 1 || rv[ri] > 1)) { + nBGR233ColoursAllocated++; + } else if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi])) { + return; + } } } gn++; @@ -507,8 +665,13 @@ bi = bn; for (ri = 0; ri < rn; ri++) { for (gi = 0; gi < gn; gi++) { - if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi])) - return; + if (appData.useBGR233 == 64 && (bv[bi] > 3 || gv[gi] > 3 || rv[ri] > 3)) { + nBGR233ColoursAllocated++; + } else if (appData.useBGR233 == 8 && (bv[bi] > 1 || gv[gi] > 1 || rv[ri] > 1)) { + nBGR233ColoursAllocated++; + } else if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi])) { + return; + } } } bn++; @@ -529,18 +692,36 @@ AllocateBGR233Colour(int r, int g, int b) { XColor c; + int bs = 6, gs = 3, rs = 0; + int bm = 3, gm = 7, rm = 7; if (nBGR233ColoursAllocated >= appData.nColours) return False; - c.red = r * 65535 / 7; - c.green = g * 65535 / 7; - c.blue = b * 65535 / 3; + if (appData.useBGR233 == 64) { + bs = 4; gs = 2, rs = 0; + bm = 3; gm = 3; rm = 3; + } + if (appData.useBGR233 == 8) { + bs = 2; gs = 1, rs = 0; + bm = 1; gm = 1; rm = 1; + } + + c.red = r * 65535 / rm; + c.green = g * 65535 / gm; + c.blue = b * 65535 / bm; + if (appData.useGreyScale) { + int ave; + ave = (c.red + c.green + c.blue)/3; + c.red = ave; + c.green = ave; + c.blue = ave; + } if (!XAllocColor(dpy, cmap, &c)) return False; - BGR233ToPixel[(b<<6) | (g<<3) | r] = c.pixel; + BGR233ToPixel[(b<<bs) | (g<<gs) | r] = c.pixel; nBGR233ColoursAllocated++; diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/corre.c vnc_unixsrc/vncviewer/corre.c --- vnc_unixsrc.orig/vncviewer/corre.c 2000-06-11 08:00:53.000000000 -0400 +++ vnc_unixsrc/vncviewer/corre.c 2008-10-05 15:16:01.000000000 -0400 @@ -29,6 +29,18 @@ #define HandleCoRREBPP CONCAT2E(HandleCoRRE,BPP) #define CARDBPP CONCAT2E(CARD,BPP) +#define FillRectangle(x, y, w, h, color) \ + { \ + XGCValues _gcv; \ + _gcv.foreground = color; \ + if (!appData.useXserverBackingStore) { \ + FillScreen(x, y, w, h, _gcv.foreground); \ + } else { \ + XChangeGC(dpy, gc, GCForeground, &_gcv); \ + XFillRectangle(dpy, desktopWin, gc, x, y, w, h); \ + } \ + } + static Bool HandleCoRREBPP (int rx, int ry, int rw, int rh) { @@ -50,11 +62,19 @@ #if (BPP == 8) gcv.foreground = (appData.useBGR233 ? BGR233ToPixel[pix] : pix); #else +#if (BPP == 16) + gcv.foreground = (appData.useBGR565 ? BGR565ToPixel[pix] : pix); +#else gcv.foreground = pix; #endif +#endif +#if 0 XChangeGC(dpy, gc, GCForeground, &gcv); XFillRectangle(dpy, desktopWin, gc, rx, ry, rw, rh); +#else + FillRectangle(rx, ry, rw, rh, gcv.foreground); +#endif if (!ReadFromRFBServer(buffer, hdr.nSubrects * (4 + (BPP / 8)))) return False; @@ -72,12 +92,22 @@ #if (BPP == 8) gcv.foreground = (appData.useBGR233 ? BGR233ToPixel[pix] : pix); #else +#if (BPP == 16) + gcv.foreground = (appData.useBGR565 ? BGR565ToPixel[pix] : pix); +#else gcv.foreground = pix; #endif +#endif +#if 0 XChangeGC(dpy, gc, GCForeground, &gcv); XFillRectangle(dpy, desktopWin, gc, rx + x, ry + y, w, h); +#else + FillRectangle(rx + x, ry + y, w, h, gcv.foreground); +#endif } return True; } + +#undef FillRectangle diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/cp_it vnc_unixsrc/vncviewer/cp_it --- vnc_unixsrc.orig/vncviewer/cp_it 1969-12-31 19:00:00.000000000 -0500 +++ vnc_unixsrc/vncviewer/cp_it 2009-03-06 20:02:05.000000000 -0500 @@ -0,0 +1,18 @@ +#!/bin/sh + +dest=/dist/bin/vncviewerz-1.3dev5-resize +suc "cp -p $dest $dest.back; mv $dest $dest.unlink; mv $dest.back $dest; rm $dest.unlink" +strip ./vncviewer +cat ./vncviewer > $dest +touch -r ./vncviewer $dest +yy=/dist/src/apps/VNC/etc/libvncserver_cvs/expts/etv/ssvnc/bin/Linux.i686/vncviewer +mv $yy $yy.unlink +cp -p ./vncviewer $yy +mv $yy.turbovnc $yy.unlink.turbovnc +cp -p ./vncviewer $HOME/etv_col/Linux.i686 +cp -p ./vncviewer.turbovnc $yy.turbovnc +cp -p ./vncviewer.turbovnc $HOME/etv_col/Linux.i686/vncviewer.turbovnc +chmod 755 $yy* + +rm -f $yy.unlink* +ls -l ./vncviewer* $dest $yy* $HOME/etv_col/Linux.i686/vncviewer* diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/cursor.c vnc_unixsrc/vncviewer/cursor.c --- vnc_unixsrc.orig/vncviewer/cursor.c 2003-01-15 04:46:52.000000000 -0500 +++ vnc_unixsrc/vncviewer/cursor.c 2010-02-25 22:04:28.000000000 -0500 @@ -38,8 +38,11 @@ static Bool prevSoftCursorSet = False; -static Pixmap rcSavedArea; -static CARD8 *rcSource, *rcMask; +static Pixmap rcSavedArea, rcSavedArea_0; +static int rcSavedArea_w = -1, rcSavedArea_h = -1; +static char *rcSavedScale = NULL; +static int rcSavedScale_len = 0; +static CARD8 *rcSource = NULL, *rcMask; static int rcHotX, rcHotY, rcWidth, rcHeight; static int rcCursorX = 0, rcCursorY = 0; static int rcLockX, rcLockY, rcLockWidth, rcLockHeight; @@ -48,8 +51,13 @@ static Bool SoftCursorInLockedArea(void); static void SoftCursorCopyArea(int oper); static void SoftCursorDraw(void); -static void FreeSoftCursor(void); -static void FreeX11Cursor(); +void FreeSoftCursor(void); +void FreeX11Cursor(); + +extern XImage *image; +extern XImage *image_scale; +extern int scale_x, scale_y; +int scale_round(int n, double factor); /* Copied from Xvnc/lib/font/util/utilbitmap.c */ static unsigned char _reverse_byte[0x100] = { @@ -91,6 +99,8 @@ static Bool prevXCursorSet = False; static Cursor prevXCursor; +extern double scale_factor_x; +extern double scale_factor_y; Bool HandleXCursor(int xhot, int yhot, int width, int height) { @@ -124,7 +134,7 @@ XQueryBestCursor(dpy, dr, width, height, &wret, &hret); } - if (width * height == 0 || wret < width || hret < height) { + if (width * height == 0 || (int) wret < width || (int) hret < height) { /* Free resources */ if (buf != NULL) free(buf); @@ -139,7 +149,7 @@ fg.green = (unsigned short)colors.foreGreen << 8 | colors.foreGreen; fg.blue = (unsigned short)colors.foreBlue << 8 | colors.foreBlue; - for (i = 0; i < bytesData * 2; i++) + for (i = 0; (size_t) i < bytesData * 2; i++) buf[i] = (char)_reverse_byte[(int)buf[i] & 0xFF]; source = XCreateBitmapFromData(dpy, dr, buf, width, height); @@ -167,148 +177,179 @@ Bool HandleCursorShape(int xhot, int yhot, int width, int height, CARD32 enc) { - int bytesPerPixel; - size_t bytesPerRow, bytesMaskData; - Drawable dr; - rfbXCursorColors rgb; - CARD32 colors[2]; - char *buf; - CARD8 *ptr; - int x, y, b; - - bytesPerPixel = myFormat.bitsPerPixel / 8; - bytesPerRow = (width + 7) / 8; - bytesMaskData = bytesPerRow * height; - dr = DefaultRootWindow(dpy); - - FreeSoftCursor(); + int bytesPerPixel; + size_t bytesPerRow, bytesMaskData; + Drawable dr; + rfbXCursorColors rgb; + CARD32 colors[2]; + char *buf; + CARD8 *ptr; + int x, y, b; + + bytesPerPixel = myFormat.bitsPerPixel / 8; + bytesPerRow = (width + 7) / 8; + bytesMaskData = bytesPerRow * height; + dr = DefaultRootWindow(dpy); - if (width * height == 0) - return True; - - /* Allocate memory for pixel data and temporary mask data. */ + FreeSoftCursor(); - rcSource = malloc(width * height * bytesPerPixel); - if (rcSource == NULL) - return False; - - buf = malloc(bytesMaskData); - if (buf == NULL) { - free(rcSource); - return False; - } + if (width * height == 0) { + return True; + } - /* Read and decode cursor pixel data, depending on the encoding type. */ + /* Allocate memory for pixel data and temporary mask data. */ - if (enc == rfbEncodingXCursor) { - if (appData.useX11Cursor) { - HandleXCursor(xhot, yhot, width, height); - return True; - } + rcSource = malloc(width * height * bytesPerPixel); + if (rcSource == NULL) { + return False; + } - /* Read and convert background and foreground colors. */ - if (!ReadFromRFBServer((char *)&rgb, sz_rfbXCursorColors)) { - free(rcSource); - free(buf); - return False; - } - colors[0] = RGB24_TO_PIXEL(32, rgb.backRed, rgb.backGreen, rgb.backBlue); - colors[1] = RGB24_TO_PIXEL(32, rgb.foreRed, rgb.foreGreen, rgb.foreBlue); + buf = malloc(bytesMaskData); + if (buf == NULL) { + free(rcSource); + rcSource = NULL; + return False; + } - /* Read 1bpp pixel data into a temporary buffer. */ - if (!ReadFromRFBServer(buf, bytesMaskData)) { - free(rcSource); - free(buf); - return False; - } + /* Read and decode cursor pixel data, depending on the encoding type. */ - /* Convert 1bpp data to byte-wide color indices. */ - ptr = rcSource; - for (y = 0; y < height; y++) { - for (x = 0; x < width / 8; x++) { - for (b = 7; b >= 0; b--) { - *ptr = buf[y * bytesPerRow + x] >> b & 1; - ptr += bytesPerPixel; - } - } - for (b = 7; b > 7 - width % 8; b--) { - *ptr = buf[y * bytesPerRow + x] >> b & 1; - ptr += bytesPerPixel; - } - } + if (enc == rfbEncodingXCursor) { + if (appData.useX11Cursor) { + HandleXCursor(xhot, yhot, width, height); + return True; + } + + /* Read and convert background and foreground colors. */ + if (!ReadFromRFBServer((char *)&rgb, sz_rfbXCursorColors)) { + free(rcSource); + rcSource = NULL; + free(buf); + return False; + } + colors[0] = RGB24_TO_PIXEL(32, rgb.backRed, rgb.backGreen, rgb.backBlue); + colors[1] = RGB24_TO_PIXEL(32, rgb.foreRed, rgb.foreGreen, rgb.foreBlue); + + /* Read 1bpp pixel data into a temporary buffer. */ + if (!ReadFromRFBServer(buf, bytesMaskData)) { + free(rcSource); + rcSource = NULL; + free(buf); + return False; + } + + /* Convert 1bpp data to byte-wide color indices. */ + ptr = rcSource; + for (y = 0; y < height; y++) { + for (x = 0; x < width / 8; x++) { + for (b = 7; b >= 0; b--) { + *ptr = buf[y * bytesPerRow + x] >> b & 1; + ptr += bytesPerPixel; + } + } + for (b = 7; b > 7 - width % 8; b--) { + *ptr = buf[y * bytesPerRow + x] >> b & 1; + ptr += bytesPerPixel; + } + } + + /* Convert indices into the actual pixel values. */ + switch (bytesPerPixel) { + case 1: + for (x = 0; x < width * height; x++) { + rcSource[x] = (CARD8)colors[rcSource[x]]; + } + break; + case 2: + for (x = 0; x < width * height; x++) { + ((CARD16 *)rcSource)[x] = (CARD16)colors[rcSource[x * 2]]; + } + break; + case 4: + for (x = 0; x < width * height; x++) { + ((CARD32 *)rcSource)[x] = colors[rcSource[x * 4]]; + } + break; + } + + } else { /* enc == rfbEncodingRichCursor */ + if (!ReadFromRFBServer((char *)rcSource, width * height * bytesPerPixel)) { + free(rcSource); + rcSource = NULL; + free(buf); + return False; + } + } - /* Convert indices into the actual pixel values. */ - switch (bytesPerPixel) { - case 1: - for (x = 0; x < width * height; x++) - rcSource[x] = (CARD8)colors[rcSource[x]]; - break; - case 2: - for (x = 0; x < width * height; x++) - ((CARD16 *)rcSource)[x] = (CARD16)colors[rcSource[x * 2]]; - break; - case 4: - for (x = 0; x < width * height; x++) - ((CARD32 *)rcSource)[x] = colors[rcSource[x * 4]]; - break; - } + /* Read and decode mask data. */ - } else { /* enc == rfbEncodingRichCursor */ + if (!ReadFromRFBServer(buf, bytesMaskData)) { + free(rcSource); + rcSource = NULL; + free(buf); + return False; + } - if (!ReadFromRFBServer((char *)rcSource, width * height * bytesPerPixel)) { - free(rcSource); - free(buf); - return False; - } + rcMask = malloc(width * height); + if (rcMask == NULL) { + free(rcSource); + rcSource = NULL; + free(buf); + return False; + } - } + ptr = rcMask; + for (y = 0; y < height; y++) { + for (x = 0; x < width / 8; x++) { + for (b = 7; b >= 0; b--) { + *ptr++ = buf[y * bytesPerRow + x] >> b & 1; + } + } + for (b = 7; b > 7 - width % 8; b--) { + *ptr++ = buf[y * bytesPerRow + x] >> b & 1; + } + } - /* Read and decode mask data. */ + free(buf); - if (!ReadFromRFBServer(buf, bytesMaskData)) { - free(rcSource); - free(buf); - return False; - } + /* Set remaining data associated with cursor. */ - rcMask = malloc(width * height); - if (rcMask == NULL) { - free(rcSource); - free(buf); - return False; - } + dr = DefaultRootWindow(dpy); - ptr = rcMask; - for (y = 0; y < height; y++) { - for (x = 0; x < width / 8; x++) { - for (b = 7; b >= 0; b--) { - *ptr++ = buf[y * bytesPerRow + x] >> b & 1; - } - } - for (b = 7; b > 7 - width % 8; b--) { - *ptr++ = buf[y * bytesPerRow + x] >> b & 1; - } - } + if (scale_x > 0) { + int w = scale_round(width, scale_factor_x) + 2; + int h = scale_round(height, scale_factor_y) + 2; + rcSavedArea = XCreatePixmap(dpy, dr, w, h, visdepth); + rcSavedArea_w = w; + rcSavedArea_h = h; + } else { + rcSavedArea = XCreatePixmap(dpy, dr, width, height, visdepth); + rcSavedArea_w = width; + rcSavedArea_h = height; + } + rcSavedArea_0 = XCreatePixmap(dpy, dr, width, height, visdepth); - free(buf); +if (0) fprintf(stderr, "rcSavedArea_wh: %d %d scale_x: %d\n", rcSavedArea_w, rcSavedArea_h, scale_x); - /* Set remaining data associated with cursor. */ + if (rcSavedScale_len < 4 * width * height + 4096) { + if (rcSavedScale) { + free(rcSavedScale); + } + rcSavedScale = (char *) malloc(2 * 4 * width * height + 4096); + } - dr = DefaultRootWindow(dpy); - rcSavedArea = XCreatePixmap(dpy, dr, width, height, visdepth); - rcHotX = xhot; - rcHotY = yhot; - rcWidth = width; - rcHeight = height; + rcHotX = xhot; + rcHotY = yhot; + rcWidth = width; + rcHeight = height; - SoftCursorCopyArea(OPER_SAVE); - SoftCursorDraw(); + SoftCursorCopyArea(OPER_SAVE); + SoftCursorDraw(); - rcCursorHidden = False; - rcLockSet = False; + rcCursorHidden = False; + rcLockSet = False; - prevSoftCursorSet = True; - return True; + prevSoftCursorSet = True; + return True; } /********************************************************************* @@ -319,20 +360,27 @@ Bool HandleCursorPos(int x, int y) { - if (appData.useX11Cursor) { - if (appData.fullScreen) - XWarpPointer(dpy, None, desktopWin, 0, 0, 0, 0, x, y); - - return True; - } + if (x < 0) x = 0; + if (y < 0) y = 0; - if (x >= si.framebufferWidth) - x = si.framebufferWidth - 1; - if (y >= si.framebufferHeight) - y = si.framebufferHeight - 1; + /* fprintf(stderr, "xy: %d %d\n", x, y); */ - SoftCursorMove(x, y); - return True; + if (x >= si.framebufferWidth) { + x = si.framebufferWidth - 1; + } + if (y >= si.framebufferHeight) { + y = si.framebufferHeight - 1; + } + + if (appData.useX11Cursor) { + if (appData.fullScreen) { + XWarpPointer(dpy, None, desktopWin, 0, 0, 0, 0, x, y); + } + return True; + } + + SoftCursorMove(x, y); + return True; } /********************************************************************* @@ -348,30 +396,31 @@ { int newX, newY; - if (!prevSoftCursorSet) - return; + if (!prevSoftCursorSet) { + return; + } - if (!rcLockSet) { - rcLockX = x; - rcLockY = y; - rcLockWidth = w; - rcLockHeight = h; - rcLockSet = True; - } else { - newX = (x < rcLockX) ? x : rcLockX; - newY = (y < rcLockY) ? y : rcLockY; - rcLockWidth = (x + w > rcLockX + rcLockWidth) ? - (x + w - newX) : (rcLockX + rcLockWidth - newX); - rcLockHeight = (y + h > rcLockY + rcLockHeight) ? - (y + h - newY) : (rcLockY + rcLockHeight - newY); - rcLockX = newX; - rcLockY = newY; - } + if (!rcLockSet) { + rcLockX = x; + rcLockY = y; + rcLockWidth = w; + rcLockHeight = h; + rcLockSet = True; + } else { + newX = (x < rcLockX) ? x : rcLockX; + newY = (y < rcLockY) ? y : rcLockY; + rcLockWidth = (x + w > rcLockX + rcLockWidth) ? + (x + w - newX) : (rcLockX + rcLockWidth - newX); + rcLockHeight = (y + h > rcLockY + rcLockHeight) ? + (y + h - newY) : (rcLockY + rcLockHeight - newY); + rcLockX = newX; + rcLockY = newY; + } - if (!rcCursorHidden && SoftCursorInLockedArea()) { - SoftCursorCopyArea(OPER_RESTORE); - rcCursorHidden = True; - } + if (!rcCursorHidden && SoftCursorInLockedArea()) { + SoftCursorCopyArea(OPER_RESTORE); + rcCursorHidden = True; + } } /********************************************************************* @@ -381,15 +430,16 @@ void SoftCursorUnlockScreen(void) { - if (!prevSoftCursorSet) - return; + if (!prevSoftCursorSet) { + return; + } - if (rcCursorHidden) { - SoftCursorCopyArea(OPER_SAVE); - SoftCursorDraw(); - rcCursorHidden = False; - } - rcLockSet = False; + if (rcCursorHidden) { + SoftCursorCopyArea(OPER_SAVE); + SoftCursorDraw(); + rcCursorHidden = False; + } + rcLockSet = False; } /********************************************************************* @@ -401,19 +451,19 @@ void SoftCursorMove(int x, int y) { - if (prevSoftCursorSet && !rcCursorHidden) { - SoftCursorCopyArea(OPER_RESTORE); - rcCursorHidden = True; - } + if (prevSoftCursorSet && !rcCursorHidden) { + SoftCursorCopyArea(OPER_RESTORE); + rcCursorHidden = True; + } - rcCursorX = x; - rcCursorY = y; + rcCursorX = x; + rcCursorY = y; - if (prevSoftCursorSet && !(rcLockSet && SoftCursorInLockedArea())) { - SoftCursorCopyArea(OPER_SAVE); - SoftCursorDraw(); - rcCursorHidden = False; - } + if (prevSoftCursorSet && !(rcLockSet && SoftCursorInLockedArea())) { + SoftCursorCopyArea(OPER_SAVE); + SoftCursorDraw(); + rcCursorHidden = False; + } } @@ -429,41 +479,169 @@ rcLockY + rcLockHeight > rcCursorY - rcHotY); } -static void SoftCursorCopyArea(int oper) -{ - int x, y, w, h; +void new_pixmap(int w, int h) { - x = rcCursorX - rcHotX; - y = rcCursorY - rcHotY; - if (x >= si.framebufferWidth || y >= si.framebufferHeight) - return; - - w = rcWidth; - h = rcHeight; - if (x < 0) { - w += x; - x = 0; - } else if (x + w > si.framebufferWidth) { - w = si.framebufferWidth - x; - } - if (y < 0) { - h += y; - y = 0; - } else if (y + h > si.framebufferHeight) { - h = si.framebufferHeight - y; - } + XFreePixmap(dpy, rcSavedArea); - if (oper == OPER_SAVE) { - /* Save screen area in memory. */ -#ifdef MITSHM - if (appData.useShm) - XSync(dpy, False); -#endif - XCopyArea(dpy, desktopWin, rcSavedArea, gc, x, y, w, h, 0, 0); - } else { - /* Restore screen area. */ - XCopyArea(dpy, rcSavedArea, desktopWin, gc, 0, 0, w, h, x, y); - } + if (w > 0 && h > 0) { + rcSavedArea = XCreatePixmap(dpy, DefaultRootWindow(dpy), w, h, visdepth); + rcSavedArea_w = w; + rcSavedArea_h = h; + + } else if (image_scale != NULL && scale_x > 0) { + int w2 = scale_round(rcWidth, scale_factor_x) + 2; + int h2 = scale_round(rcHeight, scale_factor_y) + 2; + rcSavedArea = XCreatePixmap(dpy, DefaultRootWindow(dpy), w2, h2, visdepth); + rcSavedArea_w = w2; + rcSavedArea_h = h2; + } else { + rcSavedArea = XCreatePixmap(dpy, DefaultRootWindow(dpy), rcWidth, rcHeight, visdepth); + rcSavedArea_w = rcWidth; + rcSavedArea_h = rcHeight; + } +} + +extern int XError_ign; + +static void SoftCursorCopyArea(int oper) { + int x, y, w, h; + int xs = 0, ys = 0, ws = 0, hs = 0; + static int scale_saved = 0, ss_w, ss_h; + int db = 0; + + x = rcCursorX - rcHotX; + y = rcCursorY - rcHotY; + if (x >= si.framebufferWidth || y >= si.framebufferHeight) { + return; + } + + w = rcWidth; + h = rcHeight; + if (x < 0) { + w += x; + x = 0; + } else if (x + w > si.framebufferWidth) { + w = si.framebufferWidth - x; + } + if (y < 0) { + h += y; + y = 0; + } else if (y + h > si.framebufferHeight) { + h = si.framebufferHeight - y; + } + + if (image_scale != NULL && scale_x > 0) { + xs = (int) (x * scale_factor_x); + ys = (int) (y * scale_factor_y); + ws = scale_round(w, scale_factor_x); + hs = scale_round(h, scale_factor_y); + + if (xs > 0) xs -= 1; + if (ys > 0) ys -= 1; + ws += 2; + hs += 2; + } + + XError_ign = 1; + + if (oper == OPER_SAVE) { + /* Save screen area in memory. */ + scale_saved = 0; + if (appData.useXserverBackingStore) { + XSync(dpy, False); + XCopyArea(dpy, desktopWin, rcSavedArea, gc, x, y, w, h, 0, 0); + } else { + if (image_scale != NULL && scale_x > 0) { + int Bpp = image_scale->bits_per_pixel / 8; + int Bpl = image_scale->bytes_per_line; + int i; + char *src = image_scale->data + y * Bpl + x * Bpp; + char *dst = rcSavedScale; + + if (ws > rcSavedArea_w || hs > rcSavedArea_h) { + new_pixmap(0, 0); + } + +if (db) fprintf(stderr, "save: %dx%d+%d+%d\n", ws, hs, xs, ys); + + XPutImage(dpy, rcSavedArea, gc, image, xs, ys, 0, 0, ws, hs); + + XPutImage(dpy, rcSavedArea_0, gc, image_scale, x, y, 0, 0, w, h); + + scale_saved = 1; + ss_w = ws; + ss_h = hs; + + for (i=0; i < h; i++) { + memcpy(dst, src, Bpp * w); + src += Bpl; + dst += Bpp * w; + } + } else { +if (db) fprintf(stderr, "SAVE: %dx%d+%d+%d\n", w, h, x, y); + if (w > rcSavedArea_w || h > rcSavedArea_h) { + new_pixmap(0, 0); + } + + XPutImage(dpy, rcSavedArea, gc, image, x, y, 0, 0, w, h); + } + } + } else { + +#define XE(s) if (XError_ign > 1) {fprintf(stderr, "X-%d\n", (s)); db = 1;} + + /* Restore screen area. */ + if (appData.useXserverBackingStore) { + XCopyArea(dpy, rcSavedArea, desktopWin, gc, 0, 0, w, h, x, y); +XE(1) + XGetSubImage(dpy, rcSavedArea, 0, 0, w, h, AllPlanes, ZPixmap, image, x, y); +XE(2) + + } else { + if (image_scale != NULL && scale_x > 0) { + int Bpp = image_scale->bits_per_pixel / 8; + int Bpl = image_scale->bytes_per_line; + int i; + char *dst = image_scale->data + y * Bpl + x * Bpp; + char *src = rcSavedScale; + + XCopyArea(dpy, rcSavedArea, desktopWin, gc, 0, 0, ws, hs, xs, ys); +XE(3) + XGetSubImage(dpy, rcSavedArea, 0, 0, ws, hs, AllPlanes, ZPixmap, image, xs, ys); +XE(4) +if (db) fprintf(stderr, "rstr: %dx%d+%d+%d\n", ws, hs, xs, ys); + + for (i=0; i < h; i++) { + memcpy(dst, src, Bpp * w); + src += Bpp * w; + dst += Bpl; + } + } else { + + if (scale_saved) { + XCopyArea(dpy, rcSavedArea, desktopWin, gc, 0, 0, ss_w, ss_h, x, y); +XE(5) + XGetSubImage(dpy, rcSavedArea, 0, 0, ss_w, ss_h, AllPlanes, ZPixmap, image, x, y); +XE(6) + new_pixmap(w, h); + } else { + XCopyArea(dpy, rcSavedArea, desktopWin, gc, 0, 0, w, h, x, y); +XE(7) + XGetSubImage(dpy, rcSavedArea, 0, 0, w, h, AllPlanes, ZPixmap, image, x, y); +XE(8) + } + +if (db) fprintf(stderr, "RSTR: %dx%d+%d+%d\n", w, h, x, y); + + } + } + } + + if (XError_ign > 1) { + fprintf(stderr, "XError_ign: %d, oper: %s\n", XError_ign, oper ? "restore" : "save"); + } + + XError_ign = 0; } static void SoftCursorDraw(void) @@ -472,43 +650,182 @@ int offset, bytesPerPixel; char *pos; +#define alphahack +#ifdef alphahack + /* hack to have cursor transparency at 32bpp <runge@karlrunge.com> */ + int alphablend = 0; + + if (!rcSource) { + return; + } + + if (appData.useCursorAlpha) { + alphablend = 1; + } + bytesPerPixel = myFormat.bitsPerPixel / 8; - /* FIXME: Speed optimization is possible. */ - for (y = 0; y < rcHeight; y++) { - y0 = rcCursorY - rcHotY + y; - if (y0 >= 0 && y0 < si.framebufferHeight) { - for (x = 0; x < rcWidth; x++) { - x0 = rcCursorX - rcHotX + x; - if (x0 >= 0 && x0 < si.framebufferWidth) { - offset = y * rcWidth + x; - if (rcMask[offset]) { - pos = (char *)&rcSource[offset * bytesPerPixel]; - CopyDataToScreen(pos, x0, y0, 1, 1); - } + if (alphablend && bytesPerPixel == 4) { + unsigned long pixel, put, *upos, *upix; + int got_alpha = 0, rsX, rsY, rsW, rsH; + static XImage *alpha_image = NULL; + static int iwidth = 192; + + if (! alpha_image) { + /* watch out for tiny fb (rare) */ + if (iwidth > si.framebufferWidth) { + iwidth = si.framebufferWidth; + } + if (iwidth > si.framebufferHeight) { + iwidth = si.framebufferHeight; + } + + /* initialize an XImage with a chunk of desktopWin */ + alpha_image = XGetImage(dpy, desktopWin, 0, 0, iwidth, iwidth, + AllPlanes, ZPixmap); } - } - } + + /* first check if there is any non-zero alpha channel data at all: */ + for (y = 0; y < rcHeight; y++) { + for (x = 0; x < rcWidth; x++) { + int alpha; + + offset = y * rcWidth + x; + pos = (char *)&rcSource[offset * bytesPerPixel]; + + upos = (unsigned long *) pos; + alpha = (*upos & 0xff000000) >> 24; + if (alpha) { + got_alpha = 1; + break; + } + } + if (got_alpha) { + break; + } + } + + if (!got_alpha) { + /* no alpha channel data, fallback to the old way */ + goto oldway; + } + + /* load the saved fb patch in to alpha_image (faster way?) */ + if (image_scale != NULL && scale_x > 0) { + XGetSubImage(dpy, rcSavedArea_0, 0, 0, rcWidth, rcHeight, AllPlanes, ZPixmap, alpha_image, 0, 0); + } else { + XGetSubImage(dpy, rcSavedArea, 0, 0, rcWidth, rcHeight, AllPlanes, ZPixmap, alpha_image, 0, 0); + } + + upix = (unsigned long *)alpha_image->data; + + /* if the richcursor is clipped, the fb patch will be smaller */ + rsW = rcWidth; + rsX = 0; /* used to denote a shift from the left side */ + x = rcCursorX - rcHotX; + if (x < 0) { + rsW += x; + rsX = -x; + } else if (x + rsW > si.framebufferWidth) { + rsW = si.framebufferWidth - x; + } + rsH = rcHeight; + rsY = 0; /* used to denote a shift from the top side */ + y = rcCursorY - rcHotY; + if (y < 0) { + rsH += y; + rsY = -y; + } else if (y + rsH > si.framebufferHeight) { + rsH = si.framebufferHeight - y; + } + + /* + * now loop over the cursor data, blend in the fb values, + * and then overwrite the fb (CopyDataToScreen()) + */ + for (y = 0; y < rcHeight; y++) { + y0 = rcCursorY - rcHotY + y; + if (y0 < 0 || y0 >= si.framebufferHeight) { + continue; /* clipped */ + } + for (x = 0; x < rcWidth; x++) { + int alpha, color_curs, color_fb, i; + + x0 = rcCursorX - rcHotX + x; + if (x0 < 0 || x0 >= si.framebufferWidth) { + continue; /* clipped */ + } + + offset = y * rcWidth + x; + pos = (char *)&rcSource[offset * bytesPerPixel]; + + /* extract secret alpha byte from rich cursor: */ + upos = (unsigned long *) pos; + alpha = (*upos & 0xff000000) >> 24; /* XXX MSB? */ + + /* extract the pixel from the fb: */ + pixel = *(upix + (y-rsY)*iwidth + (x-rsX)); + + put = 0; + /* for simplicity, blend all 4 bytes */ + for (i = 0; i < 4; i++) { + int sh = i*8; + color_curs = ((0xff << sh) & *upos) >> sh; + color_fb = ((0xff << sh) & pixel) >> sh; + + /* XXX assumes pre-multipled color_curs */ + color_fb = color_curs + + ((0xff - alpha) * color_fb)/0xff; + put |= color_fb << sh; + } + /* place in the fb: */ + CopyDataToScreen((char *)&put, x0, y0, 1, 1); + } + } + return; } +oldway: +#endif + + bytesPerPixel = myFormat.bitsPerPixel / 8; + + /* FIXME: Speed optimization is possible. */ + for (y = 0; y < rcHeight; y++) { + y0 = rcCursorY - rcHotY + y; + if (y0 >= 0 && y0 < si.framebufferHeight) { + for (x = 0; x < rcWidth; x++) { + x0 = rcCursorX - rcHotX + x; + if (x0 >= 0 && x0 < si.framebufferWidth) { + offset = y * rcWidth + x; + if (rcMask[offset]) { + pos = (char *)&rcSource[offset * bytesPerPixel]; + CopyDataToScreen(pos, x0, y0, 1, 1); + } + } + } + } + } + XSync(dpy, False); } -static void FreeSoftCursor(void) +void FreeSoftCursor(void) { - if (prevSoftCursorSet) { - SoftCursorCopyArea(OPER_RESTORE); - XFreePixmap(dpy, rcSavedArea); - free(rcSource); - free(rcMask); - prevSoftCursorSet = False; - } + if (prevSoftCursorSet) { + SoftCursorCopyArea(OPER_RESTORE); + XFreePixmap(dpy, rcSavedArea); + XFreePixmap(dpy, rcSavedArea_0); + free(rcSource); + rcSource = NULL; + free(rcMask); + prevSoftCursorSet = False; + } } -static void FreeX11Cursor() +void FreeX11Cursor() { - if (prevXCursorSet) { - XFreeCursor(dpy, prevXCursor); - prevXCursorSet = False; - } + if (prevXCursorSet) { + XFreeCursor(dpy, prevXCursor); + prevXCursorSet = False; + } } - diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/desktop.c vnc_unixsrc/vncviewer/desktop.c --- vnc_unixsrc.orig/vncviewer/desktop.c 2004-05-28 13:29:29.000000000 -0400 +++ vnc_unixsrc/vncviewer/desktop.c 2010-02-25 22:32:49.000000000 -0500 @@ -28,28 +28,497 @@ #include <X11/extensions/XShm.h> #endif +#include <X11/cursorfont.h> + GC gc; GC srcGC, dstGC; /* used for debugging copyrect */ Window desktopWin; -Cursor dotCursor; +Cursor dotCursor3 = None; +Cursor dotCursor4 = None; +Cursor bogoCursor = None; +Cursor waitCursor = None; Widget form, viewport, desktop; +int appshare_0_hint = -10000; +int appshare_x_hint = -10000; +int appshare_y_hint = -10000; + static Bool modifierPressed[256]; -static XImage *image = NULL; +XImage *image = NULL; +XImage *image_ycrop = NULL; +XImage *image_scale = NULL; + +int image_is_shm = 0; static Cursor CreateDotCursor(); static void CopyBGR233ToScreen(CARD8 *buf, int x, int y, int width,int height); static void HandleBasicDesktopEvent(Widget w, XtPointer ptr, XEvent *ev, Boolean *cont); +static void CopyBGR565ToScreen(CARD16 *buf, int x, int y, int width,int height); + static XtResource desktopBackingStoreResources[] = { { - XtNbackingStore, XtCBackingStore, XtRBackingStore, sizeof(int), 0, - XtRImmediate, (XtPointer) Always, + XtNbackingStore, XtCBackingStore, XtRBackingStore, sizeof(int), 0, + XtRImmediate, (XtPointer) Always, }, }; +double scale_factor_x = 0.0; +double scale_factor_y = 0.0; +int scale_x = 0, scale_y = 0; +int scale_round(int len, double fac); + +double last_rescale = 0.0; +double last_fullscreen = 0.0; +double start_time = 0.0; + +int prev_fb_width = -1; +int prev_fb_height = -1; + +void get_scale_values(double *fx, double *fy) { + char *s = appData.scale; + double f, frac_x = -1.0, frac_y = -1.0; + int n, m; + int xmax = si.framebufferWidth; + int ymax = si.framebufferHeight; + + if (appData.yCrop > 0) { + ymax = appData.yCrop; + } + + if (sscanf(s, "%d/%d", &n, &m) == 2) { + if (m == 0) { + frac_x = 1.0; + } else { + frac_x = ((double) n) / ((double) m); + } + } + if (sscanf(s, "%dx%d", &n, &m) == 2) { + frac_x = ((double) n) / ((double) xmax); + frac_y = ((double) m) / ((double) ymax); + } + if (!strcasecmp(s, "fit")) { + frac_x = ((double) dpyWidth) / ((double) xmax); + frac_y = ((double) dpyHeight) / ((double) ymax); + } + if (!strcasecmp(s, "auto")) { + Dimension w, h; + XtVaGetValues(toplevel, XtNheight, &h, XtNwidth, &w, NULL); + fprintf(stderr, "auto: %dx%d\n", w, h); + if (w > 32 && h > 32) { + frac_x = ((double) w) / ((double) xmax); + frac_y = ((double) h) / ((double) ymax); + } + } + if (frac_x < 0.0 && sscanf(s, "%lf", &f) == 1) { + if (f > 0.0) { + frac_x = f; + } + } + + if (frac_y < 0.0) { + frac_y = frac_x; + } + + if (frac_y > 0.0 && frac_x > 0.0) { + if (fx != NULL) { + *fx = frac_x; + } + if (fy != NULL) { + *fy = frac_y; + } + } else { + if (appData.scale) { + fprintf(stderr, "Invalid scale string: '%s'\n", appData.scale); + } else { + fprintf(stderr, "Invalid scale string.\n"); + } + appData.scale = NULL; + } +} + +void try_create_image(void); +void put_image(int src_x, int src_y, int dst_x, int dst_y, int width, int height, int solid); +void create_image(); + +/* toplevel -> form -> viewport -> desktop */ + +void adjust_Xt_win(int w, int h) { + int x, y, dw, dh, h0 = h; + int mw = w, mh = h; + int autoscale = 0; + + if (!appData.fullScreen && appData.scale != NULL && !strcmp(appData.scale, "auto")) { + autoscale = 1; + mw = dpyWidth; + mh = dpyHeight; + } + + if (appData.yCrop > 0) { + int ycrop = appData.yCrop; + if (image_scale && scale_factor_y > 0.0) { + ycrop = scale_round(ycrop, scale_factor_y); + if (!autoscale) { + mh = ycrop; + } + } + XtVaSetValues(toplevel, XtNmaxWidth, mw, XtNmaxHeight, mh, XtNwidth, w, XtNheight, ycrop, NULL); + XtVaSetValues(form, XtNmaxWidth, mw, XtNmaxHeight, mh, XtNwidth, w, XtNheight, ycrop, NULL); + h0 = ycrop; + } else { + XtVaSetValues(toplevel, XtNmaxWidth, mw, XtNmaxHeight, mh, XtNwidth, w, XtNheight, h, NULL); + } + + fprintf(stderr, "adjust_Xt_win: %dx%d & %dx%d\n", w, h, w, h0); + + XtVaSetValues(desktop, XtNwidth, w, XtNheight, h, NULL); + + XtResizeWidget(desktop, w, h, 0); + + if (!autoscale) { + dw = appData.wmDecorationWidth; + dh = appData.wmDecorationHeight; + + x = (dpyWidth - w - dw)/2; + y = (dpyHeight - h0 - dh)/2; + + XtConfigureWidget(toplevel, x + dw, y + dh, w, h0, 0); + } +} + +void rescale_image(void) { + double frac_x, frac_y; + int w, h; + + if (image == NULL) { + create_image(); + return; + } + + if (appData.useXserverBackingStore) { + create_image(); + return; + } + + if (image == NULL && image_scale == NULL) { + create_image(); + return; + } + + if (appData.scale == NULL) { + /* switching to not scaled */ + frac_x = frac_y = 1.0; + } else { + get_scale_values(&frac_x, &frac_y); + if (frac_x < 0.0 || frac_y < 0.0) { + create_image(); + return; + } + } + + last_rescale = dnow(); + + SoftCursorLockArea(0, 0, si.framebufferWidth, si.framebufferHeight); + + if (image_scale == NULL) { + /* switching from not scaled */ + int i; + int Bpl = image->bytes_per_line; + char *dst, *src = image->data; + + image_scale = XCreateImage(dpy, vis, visdepth, ZPixmap, 0, NULL, + si.framebufferWidth, si.framebufferHeight, BitmapPad(dpy), 0); + + image_scale->data = (char *) malloc(image_scale->bytes_per_line * image_scale->height); + + fprintf(stderr, "rescale_image: switching from not scaled. created image_scale %dx%d\n", image_scale->width, image_scale->height); + fprintf(stderr, "rescale_image: copying image -> image_scale %dx%d -> %dx%d\n", image->width, image->height, image_scale->width, image_scale->height); + + dst = image_scale->data; + + /* copy from image->data */ + for (i=0; i < image->height; i++) { + memcpy(dst, src, Bpl); + dst += Bpl; + src += Bpl; + } + } + + /* now destroy image */ + if (image && image->data) { + if (UsingShm()) { + ShmDetach(); + } + XDestroyImage(image); + fprintf(stderr, "rescale_image: destroyed 'image'\n"); + if (UsingShm()) { + ShmCleanup(); + } + image = NULL; + } + if (image_ycrop && image_ycrop->data) { + XDestroyImage(image_ycrop); + fprintf(stderr, "rescale_image: destroyed 'image_ycrop'\n"); + image_ycrop = NULL; + } + + if (frac_x == 1.0 && frac_y == 1.0) { + /* switching to not scaled */ + fprintf(stderr, "rescale_image: switching to not scaled.\n"); + w = si.framebufferWidth; + h = si.framebufferHeight; + + scale_factor_x = 0.0; + scale_factor_y = 0.0; + scale_x = 0; + scale_y = 0; + } else { + w = scale_round(si.framebufferWidth, frac_x); + h = scale_round(si.framebufferHeight, frac_y); + + scale_factor_x = frac_x; + scale_factor_y = frac_y; + scale_x = w; + scale_y = h; + } + + adjust_Xt_win(w, h); + + fprintf(stderr, "rescale: %dx%d %.4f %.4f\n", w, h, scale_factor_x, scale_factor_y); + + try_create_image(); + + if (image && image->data && image_scale && frac_x == 1.0 && frac_y == 1.0) { + /* switched to not scaled */ + int i; + int Bpl = image->bytes_per_line; + char *dst = image->data; + char *src = image_scale->data; + + fprintf(stderr, "rescale_image: switching to not scaled.\n"); + + for (i=0; i < image->height; i++) { + memcpy(dst, src, Bpl); + dst += Bpl; + src += Bpl; + } + XDestroyImage(image_scale); + fprintf(stderr, "rescale_image: destroyed 'image_scale'\n"); + image_scale = NULL; + } + + if (appData.yCrop > 0) { + int ycrop = appData.yCrop; + /* do the top part first so they can see it earlier */ + put_image(0, 0, 0, 0, si.framebufferWidth, ycrop, 0); + if (si.framebufferHeight > ycrop) { + /* this is a big fb and so will take a long time */ + if (waitCursor != None) { + XDefineCursor(dpy, desktopWin, waitCursor); + XSync(dpy, False); + } + put_image(0, 0, 0, 0, si.framebufferWidth, si.framebufferHeight - ycrop, 0); + if (waitCursor != None) { + Xcursors(1); + if (appData.useX11Cursor) { + XSetWindowAttributes attr; + unsigned long valuemask = 0; + if (appData.viewOnly) { + attr.cursor = dotCursor4; + } else { + attr.cursor = dotCursor3; + } + valuemask |= CWCursor; + XChangeWindowAttributes(dpy, desktopWin, valuemask, &attr); + } + } + } + } else { + put_image(0, 0, 0, 0, si.framebufferWidth, si.framebufferHeight, 0); + } + + SoftCursorUnlockScreen(); + + fprintf(stderr, "rescale: image_scale=%p image=%p image_ycrop=%p\n", (void *) image_scale, (void *) image, (void *) image_ycrop); + last_rescale = dnow(); + +} + +void try_create_image(void) { + + image_is_shm = 0; + if (appData.useShm) { +#ifdef MITSHM + image = CreateShmImage(0); + if (!image) { + if (appData.yCrop > 0) { + if (appData.scale != NULL && scale_x > 0) { + ; + } else { + image_ycrop = CreateShmImage(1); + if (!image_ycrop) { + appData.useShm = False; + } else { + fprintf(stderr, "created smaller image_ycrop shm image: %dx%d\n", + image_ycrop->width, image_ycrop->height); + } + } + } else { + appData.useShm = False; + } + } else { + image_is_shm = 1; + fprintf(stderr, "created shm image: %dx%d\n", image->width, image->height); + } +#endif + } + + if (!image) { + fprintf(stderr, "try_create_image: shm image create fail: image == NULL\n"); + if (scale_x > 0) { + image = XCreateImage(dpy, vis, visdepth, ZPixmap, 0, NULL, + scale_x, scale_y, BitmapPad(dpy), 0); + } else { + image = XCreateImage(dpy, vis, visdepth, ZPixmap, 0, NULL, + si.framebufferWidth, si.framebufferHeight, BitmapPad(dpy), 0); + } + + image->data = malloc(image->bytes_per_line * image->height); + + if (!image->data) { + fprintf(stderr, "try_create_image: malloc failed\n"); + exit(1); + } else { + fprintf(stderr, "try_create_image: created *non-shm* image: %dx%d\n", image->width, image->height); + } + } + fprintf(stderr, "try_create_image: image->bytes_per_line: %d\n", image->bytes_per_line); +} + +void create_image() { + image = NULL; + image_ycrop = NULL; + image_scale = NULL; + + fprintf(stderr, "create_image()\n"); + + if (CreateShmImage(-1) == NULL) { + appData.useShm = False; + } + if (appData.scale != NULL) { + if (appData.useXserverBackingStore) { + fprintf(stderr, "Cannot scale when using X11 backingstore.\n"); + } else { + double frac_x = -1.0, frac_y = -1.0; + + get_scale_values(&frac_x, &frac_y); + + if (frac_x < 0.0 || frac_y < 0.0) { + fprintf(stderr, "Cannot figure out scale factor!\n"); + goto bork; + } + + scale_factor_x = 0.0; + scale_factor_y = 0.0; + scale_x = 0; + scale_y = 0; + + + if (1) { + int w, h, hyc; + + w = scale_round(si.framebufferWidth, frac_x); + h = scale_round(si.framebufferHeight, frac_y); + hyc = h; + if (appData.yCrop > 0) { + hyc = scale_round(appData.yCrop, frac_y); + } + + /* image scale is full framebuffer */ + image_scale = XCreateImage(dpy, vis, visdepth, ZPixmap, 0, NULL, + si.framebufferWidth, si.framebufferHeight, BitmapPad(dpy), 0); + + image_scale->data = (char *) malloc(image_scale->bytes_per_line * image_scale->height); + + fprintf(stderr, "create_image: created image_scale %dx%d\n", image_scale->width, image_scale->height); + + if (!image_scale->data) { + fprintf(stderr, "create_image: malloc failed\n"); + XDestroyImage(image_scale); + fprintf(stderr, "create_image: destroyed 'image_scale'\n"); + image_scale = NULL; + } else { + int h2; + scale_factor_x = frac_x; + scale_factor_y = frac_y; + scale_x = w; + scale_y = h; + + XtVaSetValues(toplevel, XtNmaxWidth, w, XtNmaxHeight, hyc, NULL); + + h2 = scale_round(si.framebufferHeight, frac_y); + XtVaSetValues(desktop, XtNwidth, w, XtNheight, h2, NULL); + + } + fprintf(stderr, "create_image: scale: %dx%d %.4f %.4f\n", w, h, + scale_factor_x, scale_factor_y); + } + } + } + bork: + try_create_image(); +} + +int old_width = 0; +int old_height = 0; + +int guessCrop(void) { + int w = si.framebufferWidth; + + if (w == 320) { + return 240; + } else if (w == 400) { + return 300; + } else if (w == 640) { + return 480; + } else if (w == 800) { + return 600; + } else if (w == 1024) { + return 768; + } else if (w == 1152) { + return 864; + } else if (w == 1280) { + return 1024; + } else if (w == 1440) { + return 900; + } else if (w == 1600) { + return 1200; + } else if (w == 1680) { + return 1050; + } else if (w == 1920) { + return 1200; + } else { + int h = (3 * w) / 4; + return h; + } +} + +void check_tall(void) { + if (appData.appShare) { + return; + } + if (! appData.yCrop) { + int w = si.framebufferWidth; + int h = si.framebufferHeight; + if (h > 2 * w) { + fprintf(stderr, "Tall display (%dx%d) suspect 'x11vnc -ncache' mode,\n", w, h); + fprintf(stderr, " setting auto -ycrop detection.\n"); + appData.yCrop = -1; + } + } +} /* * DesktopInitBeforeRealization creates the "desktop" widget and the viewport @@ -59,91 +528,1023 @@ void DesktopInitBeforeRealization() { - int i; + int i; + int h = si.framebufferHeight; + int w = si.framebufferWidth; + double frac_x = 1.0, frac_y = 1.0; + + start_time = dnow(); + + prev_fb_width = si.framebufferWidth; + prev_fb_height = si.framebufferHeight; + + if (appData.scale != NULL) { + get_scale_values(&frac_x, &frac_y); + if (frac_x > 0.0 && frac_y > 0.0) { + w = scale_round(w, frac_x); + h = scale_round(h, frac_y); + } else { + appData.scale = NULL; + } + } - form = XtVaCreateManagedWidget("form", formWidgetClass, toplevel, - XtNborderWidth, 0, - XtNdefaultDistance, 0, NULL); + form = XtVaCreateManagedWidget("form", formWidgetClass, toplevel, + XtNborderWidth, 0, XtNdefaultDistance, 0, NULL); - viewport = XtVaCreateManagedWidget("viewport", viewportWidgetClass, form, - XtNborderWidth, 0, - NULL); + viewport = XtVaCreateManagedWidget("viewport", viewportWidgetClass, form, + XtNborderWidth, 0, NULL); - desktop = XtVaCreateManagedWidget("desktop", coreWidgetClass, viewport, - XtNborderWidth, 0, - NULL); + desktop = XtVaCreateManagedWidget("desktop", coreWidgetClass, viewport, + XtNborderWidth, 0, NULL); - XtVaSetValues(desktop, XtNwidth, si.framebufferWidth, - XtNheight, si.framebufferHeight, NULL); + XtVaSetValues(desktop, XtNwidth, w, XtNheight, h, NULL); - XtAddEventHandler(desktop, LeaveWindowMask|ExposureMask, - True, HandleBasicDesktopEvent, NULL); + XtAddEventHandler(desktop, LeaveWindowMask|EnterWindowMask|ExposureMask, + True, HandleBasicDesktopEvent, NULL); - for (i = 0; i < 256; i++) - modifierPressed[i] = False; + if (appData.yCrop) { + int hm; + if (appData.yCrop < 0) { + appData.yCrop = guessCrop(); + fprintf(stderr, "Set -ycrop to: %d\n", appData.yCrop); + } + hm = appData.yCrop; - image = NULL; + fprintf(stderr, "ycrop h: %d -> %d\n", hm, (int) (hm*frac_y)); -#ifdef MITSHM - if (appData.useShm) { - image = CreateShmImage(); - if (!image) - appData.useShm = False; - } + hm *= frac_y; + + XtVaSetValues(toplevel, XtNmaxHeight, hm, XtNheight, hm, NULL); + XtVaSetValues(form, XtNmaxHeight, hm, XtNheight, hm, NULL); + XtVaSetValues(viewport, XtNforceBars, False, NULL); + XSync(dpy, False); + } + + old_width = si.framebufferWidth; + old_height = si.framebufferHeight; + + for (i = 0; i < 256; i++) { + modifierPressed[i] = False; + } + + create_image(); +} + +#if 0 +static Widget scrollbar_y = NULL; +static int xsst = 2; #endif - if (!image) { - image = XCreateImage(dpy, vis, visdepth, ZPixmap, 0, NULL, - si.framebufferWidth, si.framebufferHeight, - BitmapPad(dpy), 0); - - image->data = malloc(image->bytes_per_line * image->height); - if (!image->data) { - fprintf(stderr,"malloc failed\n"); - exit(1); - } - } +#include <X11/Xaw/Scrollbar.h> + +#if 0 +static XtCallbackProc Scrolled(Widget w, XtPointer closure, XtPointer call_data) { + Position x, y; + XtVaGetValues(desktop, XtNx, &x, XtNy, &y, NULL); + if (0) fprintf(stderr, "scrolled by %d pixels x=%d y=%d\n", (int) call_data, x, y); + if (xsst == 2) { + x = 0; + y = 0; + XtVaSetValues(desktop, XtNx, x, XtNy, y, NULL); + } else if (xsst) { + XawScrollbarSetThumb(w, 0.0, 0.0); + } else { + float t = 0.0; + XtVaSetValues(w, XtNtopOfThumb, &t, NULL); + } + if (closure) {} } +static XtCallbackProc Jumped(Widget w, XtPointer closure, XtPointer call_data) { + float top = *((float *) call_data); + Position x, y; + XtVaGetValues(desktop, XtNx, &x, XtNy, &y, NULL); + if (0) fprintf(stderr, "thumb value: %.4f x=%d y=%d\n", top, x, y); + if (top > 0.01) { + if (xsst == 2) { + x = 0; + y = 0; + XtVaSetValues(desktop, XtNx, x, XtNy, y, NULL); + } else if (xsst) { + XawScrollbarSetThumb(w, 0.0, 0.0); + } else { + float t = 0.0, s = 1.0; + XtVaSetValues(w, XtNtopOfThumb, *(XtArgVal*)&t, XtNshown, *(XtArgVal*)&s, NULL); + } + } + if (closure) {} +} +#endif + +extern double dnow(void); + +void check_things() { + static int first = 1; + static double last_scrollbar = 0.0; + int w = si.framebufferWidth; + int h = si.framebufferHeight; + double now = dnow(); + static double last = 0; + double fac = image_scale ? scale_factor_y : 1.0; + + if (first) { + first = 0; + SendFramebufferUpdateRequest(0, 0, si.framebufferWidth, si.framebufferHeight, False); + } + if (appData.yCrop > 0 && appData.yCrop * fac < dpyHeight && h > 2*w && now > last_scrollbar + 0.25) { + Widget wv, wh, wc; + Position x0, y0; + Position x1, y1; + Dimension w0, h0, b0; + Dimension w1, h1, b1; + Dimension w2, h2, b2; + + wc = XtNameToWidget(viewport, "clip"); + wv = XtNameToWidget(viewport, "vertical"); + wh = XtNameToWidget(viewport, "horizontal"); + if (wc && wv && wh) { + int sb = appData.sbWidth; + XtVaGetValues(wv, XtNwidth, &w0, XtNheight, &h0, XtNborderWidth, &b0, XtNx, &x0, XtNy, &y0, NULL); + XtVaGetValues(wh, XtNwidth, &w1, XtNheight, &h1, XtNborderWidth, &b1, XtNx, &x1, XtNy, &y1, NULL); + XtVaGetValues(wc, XtNwidth, &w2, XtNheight, &h2, XtNborderWidth, &b2, NULL); + if (!sb) { + sb = 2; + } + if (w0 != sb || h1 != sb) { + fprintf(stderr, "Very tall (-ncache) fb, setting scrollbar thickness to: %d pixels (%d/%d)\n\n", sb, w0, h1); + + XtUnmanageChild(wv); + XtUnmanageChild(wh); + XtUnmanageChild(wc); + + XtVaSetValues(wv, XtNwidth, sb, XtNx, x0 + (w0 - sb), NULL); + XtVaSetValues(wh, XtNheight, sb, XtNy, y1 + (h1 - sb), NULL); + w2 = w2 + (w0 - sb); + h2 = h2 + (h1 - sb); + if (w2 > 10 && h2 > 10) { + XtVaSetValues(wc, XtNwidth, w2, XtNheight, h2, NULL); + } + + XtManageChild(wv); + XtManageChild(wh); + XtManageChild(wc); + + appData.sbWidth = sb; + } + } + last_scrollbar = dnow(); + } + + if (now <= last + 0.25) { + return; + } + + if (image_scale) { + scale_check_zrle(); + } + + /* e.g. xrandr resize */ + dpyWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy)); + dpyHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy)); + + if (appData.scale != NULL) { + static Dimension last_w = 0, last_h = 0; + static double last_resize = 0.0; + Dimension w, h; + if (last_w == 0) { + XtVaGetValues(toplevel, XtNwidth, &last_w, XtNheight, &last_h, NULL); + last_resize = now; + } + if (now < last_resize + 0.5) { + ; + } else if (appData.fullScreen) { + ; + } else if (!strcmp(appData.scale, "auto")) { + XtVaGetValues(toplevel, XtNwidth, &w, XtNheight, &h, NULL); + if (w < 32 || h < 32) { + ; + } else if (last_w != w || last_h != h) { + Window rr, cr, r = DefaultRootWindow(dpy); + int rx, ry, wx, wy; + unsigned int mask; + /* make sure mouse buttons not pressed */ + if (XQueryPointer(dpy, r, &rr, &cr, &rx, &ry, &wx, &wy, &mask)) { + if (mask == 0) { + rescale_image(); + last_w = w; + last_h = h; + last_resize = dnow(); + } + } + } + } + } + + last = dnow(); +} /* * DesktopInitAfterRealization does things which require the X windows to * exist. It creates some GCs and sets the dot cursor. */ +void Xcursors(int set) { + if (dotCursor3 == None) { + dotCursor3 = CreateDotCursor(3); + } + if (dotCursor4 == None) { + dotCursor4 = CreateDotCursor(4); + } + if (set) { + XSetWindowAttributes attr; + unsigned long valuemask = 0; + + if (!appData.useX11Cursor) { + if (appData.viewOnly) { + attr.cursor = dotCursor4; + } else { + attr.cursor = dotCursor3; + } + valuemask |= CWCursor; + XChangeWindowAttributes(dpy, desktopWin, valuemask, &attr); + } + } +} + void DesktopInitAfterRealization() { - XGCValues gcv; - XSetWindowAttributes attr; - unsigned long valuemask; - - desktopWin = XtWindow(desktop); - - gc = XCreateGC(dpy,desktopWin,0,NULL); - - gcv.function = GXxor; - gcv.foreground = 0x0f0f0f0f; - srcGC = XCreateGC(dpy,desktopWin,GCFunction|GCForeground,&gcv); - gcv.foreground = 0xf0f0f0f0; - dstGC = XCreateGC(dpy,desktopWin,GCFunction|GCForeground,&gcv); - - XtAddConverter(XtRString, XtRBackingStore, XmuCvtStringToBackingStore, - NULL, 0); - - XtVaGetApplicationResources(desktop, (XtPointer)&attr.backing_store, - desktopBackingStoreResources, 1, NULL); - valuemask = CWBackingStore; - - if (!appData.useX11Cursor) { - dotCursor = CreateDotCursor(); - attr.cursor = dotCursor; - valuemask |= CWCursor; - } + XGCValues gcv; + XSetWindowAttributes attr; + XWindowAttributes gattr; + unsigned long valuemask = 0; + + desktopWin = XtWindow(desktop); + + gc = XCreateGC(dpy,desktopWin,0,NULL); + + gcv.function = GXxor; + gcv.foreground = 0x0f0f0f0f; + srcGC = XCreateGC(dpy,desktopWin,GCFunction|GCForeground,&gcv); + gcv.foreground = 0xf0f0f0f0; + dstGC = XCreateGC(dpy,desktopWin,GCFunction|GCForeground,&gcv); + + XtAddConverter(XtRString, XtRBackingStore, XmuCvtStringToBackingStore, + NULL, 0); + + if (appData.useXserverBackingStore) { + Screen *s = DefaultScreenOfDisplay(dpy); + if (DoesBackingStore(s) != Always) { + fprintf(stderr, "X server does not do backingstore, disabling it.\n"); + appData.useXserverBackingStore = False; + } + } + + if (appData.useXserverBackingStore) { + XtVaGetApplicationResources(desktop, (XtPointer)&attr.backing_store, + desktopBackingStoreResources, 1, NULL); + valuemask |= CWBackingStore; + } else { + attr.background_pixel = BlackPixel(dpy, DefaultScreen(dpy)); + valuemask |= CWBackPixel; + } + + Xcursors(0); + if (!appData.useX11Cursor) { + if (appData.viewOnly) { + attr.cursor = dotCursor4; + } else { + attr.cursor = dotCursor3; + } + valuemask |= CWCursor; + } + bogoCursor = XCreateFontCursor(dpy, XC_bogosity); + waitCursor = XCreateFontCursor(dpy, XC_watch); + + XChangeWindowAttributes(dpy, desktopWin, valuemask, &attr); + + if (XGetWindowAttributes(dpy, desktopWin, &gattr)) { +#if 0 + fprintf(stderr, "desktopWin backingstore: %d save_under: %d\n", gattr.backing_store, gattr.save_under); +#endif + } + fprintf(stderr, "\n"); +} + +extern void FreeX11Cursor(void); +extern void FreeSoftCursor(void); - XChangeWindowAttributes(dpy, desktopWin, valuemask, &attr); +void +DesktopCursorOff() +{ + if (dotCursor3 == None) { + dotCursor3 = CreateDotCursor(3); + dotCursor4 = CreateDotCursor(4); + } + if (appData.viewOnly) { + XDefineCursor(dpy, desktopWin, dotCursor4); + } else { + XDefineCursor(dpy, desktopWin, dotCursor3); + } + FreeX11Cursor(); + FreeSoftCursor(); +} + + +#define CEIL(x) ( (double) ((int) (x)) == (x) ? \ + (double) ((int) (x)) : (double) ((int) (x) + 1) ) +#define FLOOR(x) ( (double) ((int) (x)) ) + +#if 0 +static int nfix(int i, int n) { + if (i < 0) { + i = 0; + } else if (i >= n) { + i = n - 1; + } + return i; } +#else +#define nfix(i, n) ( i < 0 ? 0 : ( (i >= n) ? (n - 1) : i ) ) +#endif + +int scale_round(int len, double fac) { + double eps = 0.000001; + + len = (int) (len * fac + eps); + if (len < 1) { + len = 1; + } + return len; +} + +static void scale_rect(double factor_x, double factor_y, int blend, int interpolate, + int *px, int *py, int *pw, int *ph, int solid) { + + int i, j, i1, i2, j1, j2; /* indices for scaled fb (dest) */ + int I, J, I1, I2, J1, J2; /* indices for main fb (source) */ + + double w, wx, wy, wtot; /* pixel weights */ + + double x1 = 0, y1, x2 = 0, y2; /* x-y coords for destination pixels edges */ + double dx, dy; /* size of destination pixel */ + double ddx=0, ddy=0; /* for interpolation expansion */ + + char *src, *dest; /* pointers to the two framebuffers */ + + unsigned short us = 0; + unsigned char uc = 0; + unsigned int ui = 0; + + int use_noblend_shortcut = 1; + int shrink; /* whether shrinking or expanding */ + static int constant_weights = -1, mag_int = -1; + static int last_Nx = -1, last_Ny = -1, cnt = 0; + static double last_factor = -1.0; + int b, k; + double pixave[4]; /* for averaging pixel values */ + + /* internal */ + + int X1, X2, Y1, Y2; + int Nx = si.framebufferWidth; + int Ny = si.framebufferHeight; + + int nx = scale_round(Nx, factor_x); + int ny = scale_round(Ny, factor_y); + + int Bpp = image->bits_per_pixel / 8; + int dst_bytes_per_line = image->bytes_per_line; + int src_bytes_per_line = image_scale->bytes_per_line; + + unsigned long main_red_mask = image->red_mask; + unsigned long main_green_mask = image->green_mask; + unsigned long main_blue_mask = image->blue_mask; + int mark = 1; + + char *src_fb = image_scale->data; + char *dst_fb = image->data; + + static int nosolid = -1; + int sbdy = 3; + double fmax = factor_x > factor_y ? factor_x : factor_y; +#if 0 + double fmin = factor_x < factor_y ? factor_x : factor_y; +#endif + + X1 = *px; + X2 = *px + *pw; + Y1 = *py; + Y2 = *py + *ph; + + if (fmax > 1.0) { + /* try to avoid problems with bleeding... */ + sbdy = (int) (2.0 * fmax * sbdy); + } + + /* fprintf(stderr, "scale_rect: %dx%d+%d+%d\n", *pw, *ph, *px, *py); */ + + *px = (int) (*px * factor_x); + *py = (int) (*py * factor_y); + *pw = scale_round(*pw, factor_x); + *ph = scale_round(*ph, factor_y); + + if (nosolid < 0) { + if (getenv("SSVNC_NOSOLID")) { + nosolid = 1; + } else { + nosolid = 0; + } + } + if (nosolid) solid = 0; + +#define rfbLog printf +/* Begin taken from x11vnc scale: */ + + if (factor_x <= 1.0 || factor_y <= 1.0) { + shrink = 1; + } else { + shrink = 0; + interpolate = 1; + } + + /* + * N.B. width and height (real numbers) of a scaled pixel. + * both are > 1 (e.g. 1.333 for -scale 3/4) + * they should also be equal but we don't assume it. + * + * This new way is probably the best we can do, take the inverse + * of the scaling factor to double precision. + */ + dx = 1.0/factor_x; + dy = 1.0/factor_y; + + /* + * There is some speedup if the pixel weights are constant, so + * let's special case these. + * + * If scale = 1/n and n divides Nx and Ny, the pixel weights + * are constant (e.g. 1/2 => equal on 2x2 square). + */ + if (factor_x != last_factor || Nx != last_Nx || Ny != last_Ny) { + constant_weights = -1; + mag_int = -1; + last_Nx = Nx; + last_Ny = Ny; + last_factor = factor_x; + } + + if (constant_weights < 0 && factor_x != factor_y) { + constant_weights = 0; + mag_int = 0; + } else if (constant_weights < 0) { + int n = 0; + double factor = factor_x; + + constant_weights = 0; + mag_int = 0; + + for (i = 2; i<=128; i++) { + double test = ((double) 1)/ i; + double diff, eps = 1.0e-7; + diff = factor - test; + if (-eps < diff && diff < eps) { + n = i; + break; + } + } + if (! blend || ! shrink || interpolate) { + ; + } else if (n != 0) { + if (Nx % n == 0 && Ny % n == 0) { + static int didmsg = 0; + if (mark && ! didmsg) { + didmsg = 1; + rfbLog("scale_and_mark_rect: using " + "constant pixel weight speedup " + "for 1/%d\n", n); + } + constant_weights = 1; + } + } + + n = 0; + for (i = 2; i<=32; i++) { + double test = (double) i; + double diff, eps = 1.0e-7; + diff = factor - test; + if (-eps < diff && diff < eps) { + n = i; + break; + } + } + if (! blend && factor > 1.0 && n) { + mag_int = n; + } + } +if (0) fprintf(stderr, "X1: %d Y1: %d X2: %d Y2: %d\n", X1, Y1, X2, Y2); + + if (mark && !shrink && blend) { + /* + * kludge: correct for interpolating blurring leaking + * up or left 1 destination pixel. + */ + if (X1 > 0) X1--; + if (Y1 > 0) Y1--; + } + + /* + * find the extent of the change the input rectangle induces in + * the scaled framebuffer. + */ + + /* Left edges: find largest i such that i * dx <= X1 */ + i1 = FLOOR(X1/dx); + + /* Right edges: find smallest i such that (i+1) * dx >= X2+1 */ + i2 = CEIL( (X2+1)/dx ) - 1; + + /* To be safe, correct any overflows: */ + i1 = nfix(i1, nx); + i2 = nfix(i2, nx) + 1; /* add 1 to make a rectangle upper boundary */ + + /* Repeat above for y direction: */ + j1 = FLOOR(Y1/dy); + j2 = CEIL( (Y2+1)/dy ) - 1; + + j1 = nfix(j1, ny); + j2 = nfix(j2, ny) + 1; + + /* + * special case integer magnification with no blending. + * vision impaired magnification usage is interested in this case. + */ + if (mark && ! blend && mag_int && Bpp != 3) { + int jmin, jmax, imin, imax; + + /* outer loop over *source* pixels */ + for (J=Y1; J < Y2; J++) { + jmin = J * mag_int; + jmax = jmin + mag_int; + for (I=X1; I < X2; I++) { + /* extract value */ + src = src_fb + J*src_bytes_per_line + I*Bpp; + if (Bpp == 4) { + ui = *((unsigned int *)src); + } else if (Bpp == 2) { + us = *((unsigned short *)src); + } else if (Bpp == 1) { + uc = *((unsigned char *)src); + } + imin = I * mag_int; + imax = imin + mag_int; + /* inner loop over *dest* pixels */ + for (j=jmin; j<jmax; j++) { + dest = dst_fb + j*dst_bytes_per_line + imin*Bpp; + for (i=imin; i<imax; i++) { + if (Bpp == 4) { + *((unsigned int *)dest) = ui; + } else if (Bpp == 2) { + *((unsigned short *)dest) = us; + } else if (Bpp == 1) { + *((unsigned char *)dest) = uc; + } + dest += Bpp; + } + } + } + } + goto markit; + } + + /* set these all to 1.0 to begin with */ + wx = 1.0; + wy = 1.0; + w = 1.0; + + /* + * Loop over destination pixels in scaled fb: + */ + for (j=j1; j<j2; j++) { + int jbdy = 1, I1_solid = 0; + + y1 = j * dy; /* top edge */ + if (y1 > Ny - 1) { + /* can go over with dy = 1/scale_fac */ + y1 = Ny - 1; + } + y2 = y1 + dy; /* bottom edge */ + + /* Find main fb indices covered by this dest pixel: */ + J1 = (int) FLOOR(y1); + J1 = nfix(J1, Ny); + + if (shrink && ! interpolate) { + J2 = (int) CEIL(y2) - 1; + J2 = nfix(J2, Ny); + } else { + J2 = J1 + 1; /* simple interpolation */ + ddy = y1 - J1; + } + + /* destination char* pointer: */ + dest = dst_fb + j*dst_bytes_per_line + i1*Bpp; + + if (solid) { + if (j1+sbdy <= j && j < j2-sbdy) { + jbdy = 0; + x1 = (i1+sbdy) * dx; + if (x1 > Nx - 1) { + x1 = Nx - 1; + } + I1_solid = (int) FLOOR(x1); + if (I1_solid >= Nx) I1_solid = Nx - 1; + } + } + + for (i=i1; i<i2; i++) { + int solid_skip = 0; + + if (solid) { + /* if the region is solid, we can use the noblend speedup */ + if (!jbdy && i1+sbdy <= i && i < i2-sbdy) { + solid_skip = 1; + /* pixels all the same so use X1: */ + I1 = I1_solid; + goto jsolid; + } + } + + x1 = i * dx; /* left edge */ + if (x1 > Nx - 1) { + /* can go over with dx = 1/scale_fac */ + x1 = Nx - 1; + } + x2 = x1 + dx; /* right edge */ + + /* Find main fb indices covered by this dest pixel: */ + I1 = (int) FLOOR(x1); + if (I1 >= Nx) I1 = Nx - 1; + + jsolid: + cnt++; + + if ((!blend && use_noblend_shortcut) || solid_skip) { + /* + * The noblend case involves no weights, + * and 1 pixel, so just copy the value + * directly. + */ + src = src_fb + J1*src_bytes_per_line + I1*Bpp; + if (Bpp == 4) { + *((unsigned int *)dest) + = *((unsigned int *)src); + } else if (Bpp == 2) { + *((unsigned short *)dest) + = *((unsigned short *)src); + } else if (Bpp == 1) { + *(dest) = *(src); + } else if (Bpp == 3) { + /* rare case */ + for (k=0; k<=2; k++) { + *(dest+k) = *(src+k); + } + } + dest += Bpp; + continue; + } + + if (shrink && ! interpolate) { + I2 = (int) CEIL(x2) - 1; + if (I2 >= Nx) I2 = Nx - 1; + } else { + I2 = I1 + 1; /* simple interpolation */ + ddx = x1 - I1; + } +#if 0 +if (first) fprintf(stderr, " I1=%d I2=%d J1=%d J2=%d\n", I1, I2, J1, J2); +#endif + + /* Zero out accumulators for next pixel average: */ + for (b=0; b<4; b++) { + pixave[b] = 0.0; /* for RGB weighted sums */ + } + + /* + * wtot is for accumulating the total weight. + * It should always sum to 1/(scale_fac * scale_fac). + */ + wtot = 0.0; + + /* + * Loop over source pixels covered by this dest pixel. + * + * These "extra" loops over "J" and "I" make + * the cache/cacheline performance unclear. + * For example, will the data brought in from + * src for j, i, and J=0 still be in the cache + * after the J > 0 data have been accessed and + * we are at j, i+1, J=0? The stride in J is + * main_bytes_per_line, and so ~4 KB. + * + * Typical case when shrinking are 2x2 loop, so + * just two lines to worry about. + */ + for (J=J1; J<=J2; J++) { + /* see comments for I, x1, x2, etc. below */ + if (constant_weights) { + ; + } else if (! blend) { + if (J != J1) { + continue; + } + wy = 1.0; + + /* interpolation scheme: */ + } else if (! shrink || interpolate) { + if (J >= Ny) { + continue; + } else if (J == J1) { + wy = 1.0 - ddy; + } else if (J != J1) { + wy = ddy; + } + + /* integration scheme: */ + } else if (J < y1) { + wy = J+1 - y1; + } else if (J+1 > y2) { + wy = y2 - J; + } else { + wy = 1.0; + } + + src = src_fb + J*src_bytes_per_line + I1*Bpp; + + for (I=I1; I<=I2; I++) { + + /* Work out the weight: */ + + if (constant_weights) { + ; + } else if (! blend) { + /* + * Ugh, PseudoColor colormap is + * bad news, to avoid random + * colors just take the first + * pixel. Or user may have + * specified :nb to fraction. + * The :fb will force blending + * for this case. + */ + if (I != I1) { + continue; + } + wx = 1.0; + + /* interpolation scheme: */ + } else if (! shrink || interpolate) { + if (I >= Nx) { + continue; /* off edge */ + } else if (I == I1) { + wx = 1.0 - ddx; + } else if (I != I1) { + wx = ddx; + } + + /* integration scheme: */ + } else if (I < x1) { + /* + * source left edge (I) to the + * left of dest left edge (x1): + * fractional weight + */ + wx = I+1 - x1; + } else if (I+1 > x2) { + /* + * source right edge (I+1) to the + * right of dest right edge (x2): + * fractional weight + */ + wx = x2 - I; + } else { + /* + * source edges (I and I+1) completely + * inside dest edges (x1 and x2): + * full weight + */ + wx = 1.0; + } + + w = wx * wy; + wtot += w; + + /* + * We average the unsigned char value + * instead of char value: otherwise + * the minimum (char 0) is right next + * to the maximum (char -1)! This way + * they are spread between 0 and 255. + */ + if (Bpp == 4) { + /* unroll the loops, can give 20% */ + pixave[0] += w * ((unsigned char) *(src )); + pixave[1] += w * ((unsigned char) *(src+1)); + pixave[2] += w * ((unsigned char) *(src+2)); + pixave[3] += w * ((unsigned char) *(src+3)); + } else if (Bpp == 2) { + /* + * 16bpp: trickier with green + * split over two bytes, so we + * use the masks: + */ + us = *((unsigned short *) src); + pixave[0] += w*(us & main_red_mask); + pixave[1] += w*(us & main_green_mask); + pixave[2] += w*(us & main_blue_mask); + } else if (Bpp == 1) { + pixave[0] += w * + ((unsigned char) *(src)); + } else { + for (b=0; b<Bpp; b++) { + pixave[b] += w * + ((unsigned char) *(src+b)); + } + } + src += Bpp; + } + } + + if (wtot <= 0.0) { + wtot = 1.0; + } + wtot = 1.0/wtot; /* normalization factor */ + + /* place weighted average pixel in the scaled fb: */ + if (Bpp == 4) { + *(dest ) = (char) (wtot * pixave[0]); + *(dest+1) = (char) (wtot * pixave[1]); + *(dest+2) = (char) (wtot * pixave[2]); + *(dest+3) = (char) (wtot * pixave[3]); + } else if (Bpp == 2) { + /* 16bpp / 565 case: */ + pixave[0] *= wtot; + pixave[1] *= wtot; + pixave[2] *= wtot; + us = (main_red_mask & (int) pixave[0]) + | (main_green_mask & (int) pixave[1]) + | (main_blue_mask & (int) pixave[2]); + *( (unsigned short *) dest ) = us; + } else if (Bpp == 1) { + *(dest) = (char) (wtot * pixave[0]); + } else { + for (b=0; b<Bpp; b++) { + *(dest+b) = (char) (wtot * pixave[b]); + } + } + dest += Bpp; + } + } + markit: +/* End taken from x11vnc scale: */ + if (0) {} +} + +void do_scale_stats(int width, int height) { + static double calls = 0.0, sum = 0.0, var = 0.0, last = 0.0; + double A = width * height; + + if (last == 0.0) { + last = dnow(); + } + + calls += 1.0; + sum += A; + var += A*A; + + if (dnow() > last + 4.0) { + double cnt = calls; + if (cnt <= 0.0) cnt = 1.0; + var /= cnt; + sum /= cnt; + var = var - sum * sum; + if (sum > 0.0) { + var = var / (sum*sum); + } + fprintf(stderr, "scale_rect stats: %10d %10.1f ave: %10.3f var-rat: %10.3f\n", (int) calls, sum * cnt, sum, var); + + calls = 0.0; + sum = 0.0; + var = 0.0; + last = dnow(); + } +} + +void put_image(int src_x, int src_y, int dst_x, int dst_y, int width, + int height, int solid) { + int db = 0; + int xmax = si.framebufferWidth; + int ymax = si.framebufferHeight; + +if (db || 0) fprintf(stderr, "put_image(%d %d %d %d %d %d)\n", src_x, src_y, dst_x, dst_y, width, height); + + if (image_scale) { + int i; + static int scale_stats = -1; + + for (i=0; i < 2; i++) { + if (src_x > 0) src_x--; + if (src_y > 0) src_y--; + } + for (i=0; i < 4; i++) { + if (src_x + width < xmax) width++; + if (src_y + height < ymax) height++; + } + + if (db) fprintf(stderr, "put_image(%d %d %d %d %d %d)\n", src_x, src_y, dst_x, dst_y, width, height); + if (db) fprintf(stderr, "scale_rect(%d %d %d %d)\n", src_x, src_y, width, height); + + if (scale_stats < 0) { + if (getenv("SSVNC_SCALE_STATS")) { + scale_stats = 1; + } else { + scale_stats = 0; + } + } + if (scale_stats) { + do_scale_stats(width, height); + } + + scale_rect(scale_factor_x, scale_factor_y, 1, 0, &src_x, &src_y, &width, &height, solid); + dst_x = src_x; + dst_y = src_y; + } + +#ifdef MITSHM + if (appData.useShm) { + double fac = image_scale ? scale_factor_y : 1.0; + if (image_ycrop == NULL) { + if (image_is_shm) { + XShmPutImage(dpy, desktopWin, gc, image, src_x, src_y, + dst_x, dst_y, width, height, False); + } else { + XPutImage(dpy, desktopWin, gc, image, src_x, src_y, + dst_x, dst_y, width, height); + } + } else if ((width < 32 && height < 32) || height > appData.yCrop * fac) { + XPutImage(dpy, desktopWin, gc, image, src_x, src_y, + dst_x, dst_y, width, height); + } else { + char *src, *dst; + int Bpp = image->bits_per_pixel / 8; + int Bpl = image->bytes_per_line, h; + int Bpl2 = image_ycrop->bytes_per_line; + src = image->data + src_y * Bpl + src_x * Bpp; + dst = image_ycrop->data; + for (h = 0; h < height; h++) { + memcpy(dst, src, width * Bpp); + src += Bpl; + dst += Bpl2; + } + XShmPutImage(dpy, desktopWin, gc, image_ycrop, 0, 0, + dst_x, dst_y, width, height, False); + } + } else +#endif + { + XPutImage(dpy, desktopWin, gc, image, src_x, src_y, + dst_x, dst_y, width, height); + } +} + +#if 0 +fprintf(stderr, "non-shmB image %d %d %d %d %d %d\n", src_x, src_y, dst_x, dst_y, width, height); +fprintf(stderr, "shm image_ycrop %d %d %d %d %d %d\n", 0, 0, dst_x, dst_y, width, height); +fprintf(stderr, "non-shmA image %d %d %d %d %d %d\n", src_x, src_y, dst_x, dst_y, width, height); +#endif + +void releaseAllPressedModifiers(void) { + int i; + static int debug_release = -1; + if (debug_release < 0) { + if (getenv("SSVNC_DEBUG_RELEASE")) { + debug_release = 1; + } else { + debug_release = 0; + } + } + if (debug_release) fprintf(stderr, "into releaseAllPressedModifiers()\n"); + for (i = 0; i < 256; i++) { + if (modifierPressed[i]) { + SendKeyEvent(XKeycodeToKeysym(dpy, i, 0), False); + modifierPressed[i] = False; + if (debug_release) fprintf(stderr, "releasing[%d] %s\n", i, XKeysymToString(XKeycodeToKeysym(dpy, i, 0))); + } + } +} + +#define PR_EXPOSE fprintf(stderr, "Expose: %04dx%04d+%04d+%04d %04d/%04d/%04d now: %8.4f rescale: %8.4f fullscreen: %8.4f\n", width, height, x, y, si.framebufferWidth, appData.yCrop, si.framebufferHeight, now - start_time, now - last_rescale, now - last_fullscreen); /* * HandleBasicDesktopEvent - deal with expose and leave events. @@ -152,42 +1553,529 @@ static void HandleBasicDesktopEvent(Widget w, XtPointer ptr, XEvent *ev, Boolean *cont) { - int i; + int x, y, width, height; + double now = dnow(); - switch (ev->type) { + if (w || ptr || cont) {} + + if (0) { + PR_EXPOSE; + } + + switch (ev->type) { case Expose: case GraphicsExpose: /* sometimes due to scrollbars being added/removed we get an expose outside the actual desktop area. Make sure we don't pass it on to the RFB server. */ + x = ev->xexpose.x; + y = ev->xexpose.y; + width = ev->xexpose.width; + height = ev->xexpose.height; + + if (image_scale) { + int i; + x /= scale_factor_x; + y /= scale_factor_y; + width /= scale_factor_x; + height /= scale_factor_y; + /* make them a little wider to avoid painting errors */ + for (i=0; i < 3; i++) { + if (x > 0) x--; + if (y > 0) y--; + } + for (i=0; i < 6; i++) { + if (x + width < si.framebufferWidth) width++; + if (y + height < si.framebufferHeight) height++; + } + } - if (ev->xexpose.x + ev->xexpose.width > si.framebufferWidth) { - ev->xexpose.width = si.framebufferWidth - ev->xexpose.x; - if (ev->xexpose.width <= 0) break; - } - - if (ev->xexpose.y + ev->xexpose.height > si.framebufferHeight) { - ev->xexpose.height = si.framebufferHeight - ev->xexpose.y; - if (ev->xexpose.height <= 0) break; - } - - SendFramebufferUpdateRequest(ev->xexpose.x, ev->xexpose.y, - ev->xexpose.width, ev->xexpose.height, False); - break; + if (x + width > si.framebufferWidth) { + width = si.framebufferWidth - x; + if (width <= 0) { + break; + } + } + + if (y + height > si.framebufferHeight) { + height = si.framebufferHeight - y; + if (height <= 0) { + break; + } + } + + if (appData.useXserverBackingStore) { + SendFramebufferUpdateRequest(x, y, width, height, False); + } else { + int ok = 1; + double delay = 2.5; + if (appData.fullScreen && now < last_fullscreen + delay) { + int xmax = si.framebufferWidth; + int ymax = si.framebufferHeight; + if (appData.yCrop > 0) { + ymax = appData.yCrop; + } + xmax = scale_round(xmax, scale_factor_x); + ymax = scale_round(ymax, scale_factor_y); + if (dpyWidth < xmax) { + xmax = dpyWidth; + } + if (dpyHeight < ymax) { + ymax = dpyHeight; + } + if (x != 0 && y != 0) { + ok = 0; + } + if (width < 0.9 * xmax) { + ok = 0; + } + if (height < 0.9 * ymax) { + ok = 0; + } + } + if (appData.yCrop > 0) { + if (now < last_fullscreen + delay || now < last_rescale + delay) { + if (y + height > appData.yCrop) { + height = appData.yCrop - y; + } + } + } + if (ok) { + put_image(x, y, x, y, width, height, 0); + XSync(dpy, False); + } else { + fprintf(stderr, "Skip "); + PR_EXPOSE; + } + } + break; case LeaveNotify: - for (i = 0; i < 256; i++) { - if (modifierPressed[i]) { - SendKeyEvent(XKeycodeToKeysym(dpy, i, 0), False); - modifierPressed[i] = False; - } - } - break; + releaseAllPressedModifiers(); + if (appData.fullScreen) { + fs_ungrab(1); + } + break; + case EnterNotify: + if (appData.fullScreen) { + fs_grab(1); + } + break; + case ClientMessage: + if (ev->xclient.window == XtWindow(desktop) && ev->xclient.message_type == XA_INTEGER && + ev->xclient.format == 8 && !strcmp(ev->xclient.data.b, "SendRFBUpdate")) { + SendIncrementalFramebufferUpdateRequest(); + } + break; } + check_things(); +} + +extern Position desktopX, desktopY; + +void x11vnc_appshare(char *cmd) { + char send[200], str[100]; + char *id = "cmd=id_cmd"; + int m_big = 80, m_fine = 15; + int resize = 100, db = 0; + + if (getenv("X11VNC_APPSHARE_DEBUG")) { + db = atoi(getenv("X11VNC_APPSHARE_DEBUG")); + } + + if (db) fprintf(stderr, "x11vnc_appshare: cmd=%s\n", cmd); + + str[0] = '\0'; + + if (!strcmp(cmd, "left")) { + sprintf(str, "%s:move:-%d+0", id, m_big); + } else if (!strcmp(cmd, "right")) { + sprintf(str, "%s:move:+%d+0", id, m_big); + } else if (!strcmp(cmd, "up")) { + sprintf(str, "%s:move:+0-%d", id, m_big); + } else if (!strcmp(cmd, "down")) { + sprintf(str, "%s:move:+0+%d", id, m_big); + } else if (!strcmp(cmd, "left-fine")) { + sprintf(str, "%s:move:-%d+0", id, m_fine); + } else if (!strcmp(cmd, "right-fine")) { + sprintf(str, "%s:move:+%d+0", id, m_fine); + } else if (!strcmp(cmd, "up-fine")) { + sprintf(str, "%s:move:+0-%d", id, m_fine); + } else if (!strcmp(cmd, "down-fine")) { + sprintf(str, "%s:move:+0+%d", id, m_fine); + } else if (!strcmp(cmd, "taller")) { + sprintf(str, "%s:resize:+0+%d", id, resize); + } else if (!strcmp(cmd, "shorter")) { + sprintf(str, "%s:resize:+0-%d", id, resize); + } else if (!strcmp(cmd, "wider")) { + sprintf(str, "%s:resize:+%d+0", id, resize); + } else if (!strcmp(cmd, "narrower")) { + sprintf(str, "%s:resize:-%d+0", id, resize); + } else if (!strcmp(cmd, "lower")) { + sprintf(str, "%s:lower", id); + } else if (!strcmp(cmd, "raise")) { + sprintf(str, "%s:raise", id); + } else if (!strcmp(cmd, "delete")) { + sprintf(str, "%s:wm_delete", id); + } else if (!strcmp(cmd, "position")) { + Position x, y; + int xi, yi; + + XtVaGetValues(toplevel, XtNx, &x, XtNy, &y, NULL); + xi = (int) x; + yi = (int) y; + if (appData.scale) { + double fx = 1.0, fy = 1.0; + get_scale_values(&fx, &fy); + if (fx > 0.0 && fy > 0.0) { + xi /= fx; + yi /= fx; + } + } + sprintf(str, "%s:geom:0x0+%d+%d", id, xi, yi); + fprintf(stderr, "str=%s\n", str); + } + if (strcmp(str, "")) { + Bool vo = appData.viewOnly; + strcpy(send, "X11VNC_APPSHARE_CMD:"); + strcat(send, str); + if (db) fprintf(stderr, "x11vnc_appshare: send=%s\n", send); + if (vo) appData.viewOnly = False; + SendClientCutText(send, strlen(send)); + if (vo) appData.viewOnly = True; + } +} + +void scroll_desktop(int horiz, int vert, double amount) { + Dimension h, w; + Position x, y; + Position x2, y2; + static int db = -1; + + if (db < 0) { + if (getenv("SSVNC_DEBUG_ESCAPE_KEYS")) { + db = 1; + } else { + db = 0; + } + } + + XtVaGetValues(form, XtNheight, &h, XtNwidth, &w, NULL); + XtVaGetValues(desktop, XtNx, &x, XtNy, &y, NULL); + + x2 = -x; + y2 = -y; + + if (amount == -1.0) { + int dx = horiz; + int dy = vert; + if (dx == 0 && dy == 0) { + return; + } + x2 -= dx; + y2 -= dy; + } else { + if (horiz) { + int dx = (int) (amount * w); + if (dx < 0) dx = -dx; + if (amount == 0.0) dx = 1; + if (horiz > 0) { + x2 += dx; + } else { + x2 -= dx; + } + if (x2 < 0) x2 = 0; + } + if (vert) { + int dy = (int) (amount * h); + if (amount == 0.0) dy = 1; + if (dy < 0) dy = -dy; + if (vert < 0) { + y2 += dy; + } else { + y2 -= dy; + } + if (y2 < 0) y2 = 0; + } + } + + if (db) fprintf(stderr, "%d %d %f viewport(%dx%d): %d %d -> %d %d\n", horiz, vert, amount, w, h, -x, -y, x2, y2); + XawViewportSetCoordinates(viewport, x2, y2); + + if (appData.fullScreen) { + XSync(dpy, False); + XtVaGetValues(desktop, XtNx, &x, XtNy, &y, NULL); + desktopX = -x; + desktopY = -y; + } else if (amount == -1.0) { + XSync(dpy, False); + } } +void scale_desktop(int bigger, double frac) { + double current, new; + char tmp[100]; + char *s; + int fs; + + if (appData.scale == NULL) { + s = "1.0"; + } else { + s = appData.scale; + } + if (!strcmp(s, "auto")) { + fprintf(stderr, "scale_desktop: skipping scale mode '%s'\n", s); + return; + } else if (!strcmp(s, "fit")) { + fprintf(stderr, "scale_desktop: skipping scale mode '%s'\n", s); + return; + } else if (strstr(s, "x")) { + fprintf(stderr, "scale_desktop: skipping scale mode '%s'\n", s); + return; + } else if (!strcmp(s, "none")) { + s = "1.0"; + } + + if (sscanf(s, "%lf", ¤t) != 1) { + fprintf(stderr, "scale_desktop: skipping scale mode '%s'\n", s); + return; + } + if (bigger) { + new = current * (1.0 + frac); + } else { + new = current / (1.0 + frac); + } + if (0.99 < new && new < 1.01) { + new = 1.0; + } + if (new > 5.0) { + fprintf(stderr, "scale_desktop: not scaling > 5.0: %f\n", new); + return; + } else if (new < 0.05) { + fprintf(stderr, "scale_desktop: not scaling < 0.05: %f\n", new); + return; + } + sprintf(tmp, "%.16f", new); + appData.scale = strdup(tmp); + + fs = 0; + if (appData.fullScreen) { + fs = 1; + FullScreenOff(); + } + if (1) { + double fx, fy; + get_scale_values(&fx, &fy); + if (fx > 0.0 && fy > 0.0) { + rescale_image(); + } + } + if (fs) { + FullScreenOn(); + } +} + +static int escape_mods[8]; +static int escape_drag_in_progress = 0, last_x = 0, last_y = 0; +static double last_drag = 0.0; +static double last_key = 0.0; + +static int escape_sequence_pressed(void) { + static char *prev = NULL; + char *str = "default"; + int sum, i, init = 0, pressed; + static int db = -1; + + if (db < 0) { + if (getenv("SSVNC_DEBUG_ESCAPE_KEYS")) { + db = 1; + } else { + db = 0; + } + } + + if (appData.escapeKeys != NULL) { + str = appData.escapeKeys; + } + if (prev == NULL) { + init = 1; + prev = strdup(str); + } else { + if (strcmp(prev, str)) { + init = 1; + free(prev); + prev = strdup(str); + } + } + if (db) fprintf(stderr, "str: %s\n", str); + + if (init) { + char *p, *s; + KeySym ks; + int k = 0, failed = 0; + + for (i = 0; i < 8; i++) { + escape_mods[i] = -1; + } + + if (!strcasecmp(str, "default")) { +#if (defined(__MACH__) && defined(__APPLE__)) + s = strdup("Control_L,Meta_L"); +#else + s = strdup("Alt_L,Super_L"); +#endif + } else { + s = strdup(str); + } + + p = strtok(s, ",+ "); + while (p) { + ks = XStringToKeysym(p); + if (ks == XK_Shift_L || ks == XK_Shift_R) { + putenv("NO_X11VNC_APPSHARE=1"); + } + if (k >= 8) { + fprintf(stderr, "EscapeKeys: more than 8 modifier keys.\n"); + failed = 1; + break; + } + if (ks == NoSymbol) { + fprintf(stderr, "EscapeKeys: failed lookup for '%s'\n", p); + failed = 1; + break; + } else if (!IsModifierKey(ks)) { + fprintf(stderr, "EscapeKeys: not a modifier key '%s'\n", p); + failed = 1; + break; + } else { + KeyCode kc = XKeysymToKeycode(dpy, ks); + if (kc == NoSymbol) { + fprintf(stderr, "EscapeKeys: no keycode for modifier key '%s'\n", p); + failed = 1; + break; + } + if (db) fprintf(stderr, "set: %d %d\n", k, kc); + escape_mods[k++] = kc; + } + + p = strtok(NULL, ",+ "); + } + free(s); + + if (failed) { + for (i = 0; i < 8; i++) { + escape_mods[i] = -1; + } + } + } + + pressed = 1; + sum = 0; + for (i = 0; i < 8; i++) { + int kc = escape_mods[i]; + if (kc != -1 && kc < 256) { + if (db) fprintf(stderr, "try1: %d %d = %d\n", i, kc, modifierPressed[kc]); + if (!modifierPressed[kc]) { + pressed = 0; + break; + } else { + sum++; + } + } + } + if (sum == 0) pressed = 0; + + if (!pressed) { + /* user may have dragged mouse outside of toplevel window */ + int i, k; + int keystate[256]; + char keys[32]; + + /* so query server instead of modifierPressed[] */ + XQueryKeymap(dpy, keys); + for (i=0; i<32; i++) { + char c = keys[i]; + + for (k=0; k < 8; k++) { + if (c & 0x1) { + keystate[8*i + k] = 1; + } else { + keystate[8*i + k] = 0; + } + c = c >> 1; + } + } + + /* check again using keystate[] */ + pressed = 2; + sum = 0; + for (i = 0; i < 8; i++) { + int kc = escape_mods[i]; + if (kc != -1 && kc < 256) { + if (db) fprintf(stderr, "try2: %d %d = %d\n", i, kc, keystate[kc]); + if (!keystate[kc]) { + pressed = 0; + break; + } else { + sum++; + } + } + } + if (sum == 0) pressed = 0; + } + + return pressed; +} + +static int shift_is_down(void) { + int shift_down = 0; + KeyCode kc; + + if (appData.viewOnly) { + int i, k; + char keys[32]; + int keystate[256]; + + XQueryKeymap(dpy, keys); + for (i=0; i<32; i++) { + char c = keys[i]; + + for (k=0; k < 8; k++) { + if (c & 0x1) { + keystate[8*i + k] = 1; + } else { + keystate[8*i + k] = 0; + } + c = c >> 1; + } + } + + kc = XKeysymToKeycode(dpy, XK_Shift_L); + if (kc != NoSymbol && keystate[kc]) { + shift_down = 1; + } else { + kc = XKeysymToKeycode(dpy, XK_Shift_R); + if (kc != NoSymbol && keystate[kc]) { + shift_down = 1; + } + } + return shift_down; + } else { + kc = XKeysymToKeycode(dpy, XK_Shift_L); + if (kc != NoSymbol && modifierPressed[kc]) { + shift_down = 1; + } else { + kc = XKeysymToKeycode(dpy, XK_Shift_R); + if (kc != NoSymbol && modifierPressed[kc]) { + shift_down = 1; + } + } + return shift_down; + } +} + /* * SendRFBEvent is an action which sends an RFB event. It can be used in two * ways. Without any parameters it simply sends an RFB event corresponding to @@ -201,127 +2089,406 @@ * button2 down, 3 for both, etc). */ +extern Bool selectingSingleWindow; + +extern Cursor dotCursor3; +extern Cursor dotCursor4; + +extern void set_server_scale(int); + void SendRFBEvent(Widget w, XEvent *ev, String *params, Cardinal *num_params) { - KeySym ks; - char keyname[256]; - int buttonMask, x, y; - - if (appData.fullScreen && ev->type == MotionNotify) { - if (BumpScroll(ev)) - return; - } + KeySym ks; + char keyname[256]; + int buttonMask, x, y; + int do_escape; + static int db = -1; + char *ek = appData.escapeKeys; + + if (db < 0) { + if (getenv("SSVNC_DEBUG_ESCAPE_KEYS")) { + db = 1; + } else { + db = 0; + } + } + + if (ev->type == MotionNotify || ev->type == KeyRelease) { + static double last = 0.0; + double now = dnow(); + if (now > last + 0.25) { + check_things(); + last = now; + } + } + + if (selectingSingleWindow && ev->type == ButtonPress) { + selectingSingleWindow = False; + SendSingleWindow(ev->xbutton.x, ev->xbutton.y); + if (appData.viewOnly) { + XDefineCursor(dpy, desktopWin, dotCursor4); + } else { + XDefineCursor(dpy, desktopWin, dotCursor3); + } + return; + } - if (appData.viewOnly) return; + if (appData.fullScreen && ev->type == MotionNotify && !escape_drag_in_progress) { + if (BumpScroll(ev)) { + return; + } + } + + do_escape = 0; + if (ek != NULL && (ek[0] == 'n' || ek[0] == 'N') && !strcasecmp(ek, "never")) { + ; + } else if (appData.viewOnly) { + do_escape = 1; + } else if (appData.escapeActive) { + int skip = 0, is_key = 0; + + if (ev->type == KeyPress || ev->type == KeyRelease) { + is_key = 1; + XLookupString(&ev->xkey, keyname, 256, &ks, NULL); + if (IsModifierKey(ks)) { + skip = 1; + } + } + if (!skip) { + int es = escape_sequence_pressed(); + if (es == 1) { + do_escape = 1; + } else if (es == 2) { + if (is_key) { + if (dnow() < last_key + 5.0) { + do_escape = 1; + } + } else { + if (dnow() < last_drag + 5.0) { + do_escape = 1; + } + } + } + } + } + if (!do_escape) { + escape_drag_in_progress = 0; + } + if (db) fprintf(stderr, "do_escape: %d\n", do_escape); + + if (do_escape) { + int W = si.framebufferWidth; + int H = si.framebufferHeight; + int shift_down = 0; + + if (!getenv("NO_X11VNC_APPSHARE")) { + shift_down = shift_is_down(); + } + if (db) fprintf(stderr, "shift_down: %d\n", shift_down); + + if (*num_params != 0) { + if (strcasecmp(params[0],"fbupdate") == 0) { + SendFramebufferUpdateRequest(0, 0, W, H, False); + } + } + if (ev->type == ButtonRelease) { + XButtonEvent *b = (XButtonEvent *) ev; + if (db) fprintf(stderr, "ButtonRelease: %d %d %d\n", b->x_root, b->y_root, b->state); + if (b->button == 3) { + if (shift_down) { + x11vnc_appshare("delete"); + } else { + ShowPopup(w, ev, params, num_params); + } + } else if (escape_drag_in_progress && b->button == 1) { + escape_drag_in_progress = 0; + } + } else if (ev->type == ButtonPress) { + XButtonEvent *b = (XButtonEvent *) ev; + if (db) fprintf(stderr, "ButtonPress: %d %d %d\n", b->x_root, b->y_root, b->state); + if (b->button == 1) { + if (shift_down) { + x11vnc_appshare("position"); + } else { + escape_drag_in_progress = 1; + last_x = b->x_root; + last_y = b->y_root; + } + } else { + escape_drag_in_progress = 0; + } + } else if (ev->type == MotionNotify) { + XMotionEvent *m = (XMotionEvent *) ev; + if (escape_drag_in_progress) { + if (db) fprintf(stderr, "MotionNotify: %d %d %d\n", m->x_root, m->y_root, m->state); + scroll_desktop(m->x_root - last_x, m->y_root - last_y, -1.0); + last_x = m->x_root; + last_y = m->y_root; + } + } else if (ev->type == KeyRelease) { + int did = 1; + + XLookupString(&ev->xkey, keyname, 256, &ks, NULL); + if (ks == XK_1 || ks == XK_KP_1) { + set_server_scale(1); + } else if (ks == XK_2 || ks == XK_KP_2) { + set_server_scale(2); + } else if (ks == XK_3 || ks == XK_KP_3) { + set_server_scale(3); + } else if (ks == XK_4 || ks == XK_KP_4) { + set_server_scale(4); + } else if (ks == XK_5 || ks == XK_KP_5) { + set_server_scale(5); + } else if (ks == XK_6 || ks == XK_KP_6) { + set_server_scale(6); + } else if (ks == XK_r || ks == XK_R) { + SendFramebufferUpdateRequest(0, 0, W, H, False); + } else if (ks == XK_b || ks == XK_B) { + ToggleBell(w, ev, params, num_params); + } else if (ks == XK_c || ks == XK_C) { + Toggle8bpp(w, ev, params, num_params); + } else if (ks == XK_x || ks == XK_X) { + ToggleX11Cursor(w, ev, params, num_params); + } else if (ks == XK_z || ks == XK_Z) { + ToggleTightZRLE(w, ev, params, num_params); + } else if (ks == XK_h || ks == XK_H) { + ToggleTightHextile(w, ev, params, num_params); + } else if (ks == XK_f || ks == XK_F) { + ToggleFileXfer(w, ev, params, num_params); + } else if (ks == XK_V) { + ToggleViewOnly(w, ev, params, num_params); + } else if (ks == XK_Q) { + Quit(w, ev, params, num_params); + } else if (ks == XK_l || ks == XK_L) { + ToggleFullScreen(w, ev, params, num_params); + } else if (ks == XK_a || ks == XK_A) { + ToggleCursorAlpha(w, ev, params, num_params); + } else if (ks == XK_s || ks == XK_S) { + SetScale(w, ev, params, num_params); + } else if (ks == XK_t || ks == XK_T) { + ToggleTextChat(w, ev, params, num_params); + } else if (ks == XK_e || ks == XK_E) { + SetEscapeKeys(w, ev, params, num_params); + } else if (ks == XK_g || ks == XK_G) { + ToggleXGrab(w, ev, params, num_params); + } else if (ks == XK_D) { + if (shift_down || appData.appShare) { + x11vnc_appshare("delete"); + } + } else if (ks == XK_M) { + if (shift_down || appData.appShare) { + x11vnc_appshare("position"); + } + } else if (ks == XK_Left) { + if (shift_down) { + x11vnc_appshare("left"); + } else { + scroll_desktop(-1, 0, 0.1); + } + } else if (ks == XK_Right) { + if (shift_down) { + x11vnc_appshare("right"); + } else { + scroll_desktop(+1, 0, 0.1); + } + } else if (ks == XK_Up) { + if (shift_down) { + x11vnc_appshare("up"); + } else { + scroll_desktop(0, +1, 0.1); + } + } else if (ks == XK_Down) { + if (shift_down) { + x11vnc_appshare("down"); + } else { + scroll_desktop(0, -1, 0.1); + } + } else if (ks == XK_KP_Left) { + if (shift_down) { + x11vnc_appshare("left-fine"); + } else { + scroll_desktop(-1, 0, 0.0); + } + } else if (ks == XK_KP_Right) { + if (shift_down) { + x11vnc_appshare("right-fine"); + } else { + scroll_desktop(+1, 0, 0.0); + } + } else if (ks == XK_KP_Up) { + if (shift_down) { + x11vnc_appshare("up-fine"); + } else { + scroll_desktop(0, +1, 0.0); + } + } else if (ks == XK_KP_Down) { + if (shift_down) { + x11vnc_appshare("down-fine"); + } else { + scroll_desktop(0, -1, 0.0); + } + } else if (ks == XK_Next || ks == XK_KP_Next) { + if (shift_down && ks == XK_Next) { + x11vnc_appshare("shorter"); + } else { + scroll_desktop(0, -1, 1.0); + } + } else if (ks == XK_Prior || ks == XK_KP_Prior) { + if (shift_down && ks == XK_Prior) { + x11vnc_appshare("taller"); + } else { + scroll_desktop(0, +1, 1.0); + } + } else if (ks == XK_End || ks == XK_KP_End) { + if (shift_down && ks == XK_End) { + x11vnc_appshare("narrower"); + } else { + scroll_desktop(+1, 0, 1.0); + } + } else if (ks == XK_Home || ks == XK_KP_Home) { + if (shift_down && ks == XK_Home) { + x11vnc_appshare("wider"); + } else { + scroll_desktop(-1, 0, 1.0); + } + } else if (ks == XK_equal || ks == XK_plus) { + if (shift_down) { + x11vnc_appshare("raise"); + } else { + scale_desktop(1, 0.1); + } + } else if (ks == XK_underscore || ks == XK_minus) { + if (shift_down) { + x11vnc_appshare("lower"); + } else { + scale_desktop(0, 0.1); + } + } else { + did = 0; + } + if (did) { + last_key = dnow(); + } + } + if (escape_drag_in_progress) { + last_drag = dnow(); + } + return; + } + if (appData.viewOnly) { + return; + } + + if (*num_params != 0) { + if (strncasecmp(params[0],"key",3) == 0) { + if (*num_params != 2) { + fprintf(stderr, "Invalid params: " + "SendRFBEvent(key|keydown|keyup,<keysym>)\n"); + return; + } + ks = XStringToKeysym(params[1]); + if (ks == NoSymbol) { + fprintf(stderr,"Invalid keysym '%s' passed to " + "SendRFBEvent\n", params[1]); + return; + } + if (strcasecmp(params[0],"keydown") == 0) { + SendKeyEvent(ks, 1); + } else if (strcasecmp(params[0],"keyup") == 0) { + SendKeyEvent(ks, 0); + } else if (strcasecmp(params[0],"key") == 0) { + SendKeyEvent(ks, 1); + SendKeyEvent(ks, 0); + } else { + fprintf(stderr,"Invalid event '%s' passed to " + "SendRFBEvent\n", params[0]); + return; + } + } else if (strcasecmp(params[0],"fbupdate") == 0) { + if (*num_params != 1) { + fprintf(stderr, "Invalid params: " + "SendRFBEvent(fbupdate)\n"); + return; + } + SendFramebufferUpdateRequest(0, 0, si.framebufferWidth, + si.framebufferHeight, False); + + } else if (strcasecmp(params[0],"ptr") == 0) { + if (*num_params == 4) { + x = atoi(params[1]); + y = atoi(params[2]); + buttonMask = atoi(params[3]); + SendPointerEvent(x, y, buttonMask); + } else if (*num_params == 2) { + switch (ev->type) { + case ButtonPress: + case ButtonRelease: + x = ev->xbutton.x; + y = ev->xbutton.y; + break; + case KeyPress: + case KeyRelease: + x = ev->xkey.x; + y = ev->xkey.y; + break; + default: + fprintf(stderr, "Invalid event caused " + "SendRFBEvent(ptr,<buttonMask>)\n"); + return; + } + buttonMask = atoi(params[1]); + SendPointerEvent(x, y, buttonMask); + } else { + fprintf(stderr, "Invalid params: " + "SendRFBEvent(ptr,<x>,<y>,<buttonMask>)\n" + " or SendRFBEvent(ptr,<buttonMask>)\n"); + return; + } + } else { + fprintf(stderr,"Invalid event '%s' passed to " + "SendRFBEvent\n", params[0]); + } + return; + } - if (*num_params != 0) { - if (strncasecmp(params[0],"key",3) == 0) { - if (*num_params != 2) { - fprintf(stderr, - "Invalid params: SendRFBEvent(key|keydown|keyup,<keysym>)\n"); - return; - } - ks = XStringToKeysym(params[1]); - if (ks == NoSymbol) { - fprintf(stderr,"Invalid keysym '%s' passed to SendRFBEvent\n", - params[1]); - return; - } - if (strcasecmp(params[0],"keydown") == 0) { - SendKeyEvent(ks, 1); - } else if (strcasecmp(params[0],"keyup") == 0) { - SendKeyEvent(ks, 0); - } else if (strcasecmp(params[0],"key") == 0) { - SendKeyEvent(ks, 1); - SendKeyEvent(ks, 0); - } else { - fprintf(stderr,"Invalid event '%s' passed to SendRFBEvent\n", - params[0]); - return; - } - } else if (strcasecmp(params[0],"fbupdate") == 0) { - if (*num_params != 1) { - fprintf(stderr, "Invalid params: SendRFBEvent(fbupdate)\n"); - return; - } - SendFramebufferUpdateRequest(0, 0, si.framebufferWidth, - si.framebufferHeight, False); - } else if (strcasecmp(params[0],"ptr") == 0) { - if (*num_params == 4) { - x = atoi(params[1]); - y = atoi(params[2]); - buttonMask = atoi(params[3]); - SendPointerEvent(x, y, buttonMask); - } else if (*num_params == 2) { switch (ev->type) { + case MotionNotify: + while (XCheckTypedWindowEvent(dpy, desktopWin, MotionNotify, ev)) { + ; /* discard all queued motion notify events */ + } + + SendPointerEvent(ev->xmotion.x, ev->xmotion.y, + (ev->xmotion.state & 0x1f00) >> 8); + return; + case ButtonPress: + SendPointerEvent(ev->xbutton.x, ev->xbutton.y, + (((ev->xbutton.state & 0x1f00) >> 8) | + (1 << (ev->xbutton.button - 1)))); + return; + case ButtonRelease: - x = ev->xbutton.x; - y = ev->xbutton.y; - break; + SendPointerEvent(ev->xbutton.x, ev->xbutton.y, + (((ev->xbutton.state & 0x1f00) >> 8) & + ~(1 << (ev->xbutton.button - 1)))); + return; + case KeyPress: case KeyRelease: - x = ev->xkey.x; - y = ev->xkey.y; - break; - default: - fprintf(stderr, - "Invalid event caused SendRFBEvent(ptr,<buttonMask>)\n"); - return; - } - buttonMask = atoi(params[1]); - SendPointerEvent(x, y, buttonMask); - } else { - fprintf(stderr, - "Invalid params: SendRFBEvent(ptr,<x>,<y>,<buttonMask>)\n" - " or SendRFBEvent(ptr,<buttonMask>)\n"); - return; - } - - } else { - fprintf(stderr,"Invalid event '%s' passed to SendRFBEvent\n", params[0]); - } - return; - } - - switch (ev->type) { + XLookupString(&ev->xkey, keyname, 256, &ks, NULL); - case MotionNotify: - while (XCheckTypedWindowEvent(dpy, desktopWin, MotionNotify, ev)) - ; /* discard all queued motion notify events */ - - SendPointerEvent(ev->xmotion.x, ev->xmotion.y, - (ev->xmotion.state & 0x1f00) >> 8); - return; - - case ButtonPress: - SendPointerEvent(ev->xbutton.x, ev->xbutton.y, - (((ev->xbutton.state & 0x1f00) >> 8) | - (1 << (ev->xbutton.button - 1)))); - return; - - case ButtonRelease: - SendPointerEvent(ev->xbutton.x, ev->xbutton.y, - (((ev->xbutton.state & 0x1f00) >> 8) & - ~(1 << (ev->xbutton.button - 1)))); - return; - - case KeyPress: - case KeyRelease: - XLookupString(&ev->xkey, keyname, 256, &ks, NULL); - - if (IsModifierKey(ks)) { - ks = XKeycodeToKeysym(dpy, ev->xkey.keycode, 0); - modifierPressed[ev->xkey.keycode] = (ev->type == KeyPress); - } + if (IsModifierKey(ks)) { + ks = XKeycodeToKeysym(dpy, ev->xkey.keycode, 0); + modifierPressed[ev->xkey.keycode] = (ev->type == KeyPress); + } - SendKeyEvent(ks, (ev->type == KeyPress)); - return; + SendKeyEvent(ks, (ev->type == KeyPress)); + return; - default: - fprintf(stderr,"Invalid event passed to SendRFBEvent\n"); - } + default: + fprintf(stderr,"Invalid event passed to SendRFBEvent\n"); + } } @@ -329,68 +2496,255 @@ * CreateDotCursor. */ +#ifndef very_small_dot_cursor static Cursor -CreateDotCursor() +CreateDotCursor(int which) { - Cursor cursor; - Pixmap src, msk; - static char srcBits[] = { 0, 14,14,14, 0 }; - static char mskBits[] = { 14,31,31,31,14 }; - XColor fg, bg; - - src = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), srcBits, 5, 5); - msk = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), mskBits, 5, 5); - XAllocNamedColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), "black", - &fg, &fg); - XAllocNamedColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), "white", - &bg, &bg); - cursor = XCreatePixmapCursor(dpy, src, msk, &fg, &bg, 2, 2); - XFreePixmap(dpy, src); - XFreePixmap(dpy, msk); + Cursor cursor; + Pixmap src, msk; + static char srcBits3[] = { 0x00, 0x02, 0x00 }; + static char mskBits3[] = { 0x02, 0x07, 0x02 }; + static char srcBits4[] = { 0x00, 0x06, 0x06, 0x00 }; + static char mskBits4[] = { 0x06, 0x0f, 0x0f, 0x06 }; + XColor fg, bg; + + if (which == 3) { + src = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), srcBits3, 3, 3); + msk = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), mskBits3, 3, 3); + } else { + src = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), srcBits4, 4, 4); + msk = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), mskBits4, 4, 4); + } + XAllocNamedColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), "black", + &fg, &fg); + XAllocNamedColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), "white", + &bg, &bg); + cursor = XCreatePixmapCursor(dpy, src, msk, &fg, &bg, 1, 1); + XFreePixmap(dpy, src); + XFreePixmap(dpy, msk); - return cursor; + return cursor; } +#else +static Cursor +CreateDotCursor() +{ + Cursor cursor; + Pixmap src, msk; + static char srcBits[] = { 0, 14, 0 }; + static char mskBits[] = { 14,31,14 }; + XColor fg, bg; + + src = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), srcBits, 3, 3); + msk = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), mskBits, 3, 3); + XAllocNamedColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), "black", + &fg, &fg); + XAllocNamedColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), "white", + &bg, &bg); + cursor = XCreatePixmapCursor(dpy, src, msk, &fg, &bg, 1, 1); + XFreePixmap(dpy, src); + XFreePixmap(dpy, msk); + return cursor; +} +#endif +int skip_maybe_sync = 0; +void maybe_sync(int width, int height) { + static int singles = 0, always_skip = -1; + int singles_max = 64; + + if (always_skip < 0) { + if (getenv("SSVNC_NO_MAYBE_SYNC")) { + always_skip = 1; + } else { + always_skip = 0; + } + } + if (skip_maybe_sync || always_skip) { + return; + } +#if 0 + if (width > 1 || height > 1) { + XSync(dpy, False); + singles = 0; + } else { + if (++singles >= singles_max) { + singles = 0; + XSync(dpy, False); + } + } +#else + if (width * height >= singles_max) { + XSync(dpy, False); + singles = 0; + } else { + singles += width * height; + if (singles >= singles_max) { + XSync(dpy, False); + singles = 0; + } + } +#endif +} /* - * CopyDataToScreen. + * FillImage. */ void -CopyDataToScreen(char *buf, int x, int y, int width, int height) +FillScreen(int x, int y, int width, int height, unsigned long fill) { - if (appData.rawDelay != 0) { - XFillRectangle(dpy, desktopWin, gc, x, y, width, height); + XImage *im = image_scale ? image_scale : image; + int bpp = im->bits_per_pixel; + int Bpp = im->bits_per_pixel / 8; + int Bpl = im->bytes_per_line; + int h, widthInBytes = width * Bpp; + static char *buf = NULL; + static int buflen = 0; + unsigned char *ucp; + unsigned short *usp; + unsigned int *uip; + char *scr; + int b0, b1, b2; - XSync(dpy,False); +#if 0 +fprintf(stderr, "FillImage bpp=%d %04dx%04d+%04d+%04d -- 0x%x\n", bpp, width, height, x, y, fill); +#endif + if (appData.chatOnly) { + return; + } - usleep(appData.rawDelay * 1000); - } + if (widthInBytes > buflen || !buf) { + if (buf) { + free(buf); + } + buflen = widthInBytes * 2; + buf = (char *)malloc(buflen); + } + ucp = (unsigned char*) buf; + usp = (unsigned short*) buf; + uip = (unsigned int*) buf; + + if (isLSB) { + b0 = 0; b1 = 1; b2 = 2; + } else { + b0 = 2; b1 = 1; b2 = 0; + } - if (!appData.useBGR233) { - int h; - int widthInBytes = width * myFormat.bitsPerPixel / 8; - int scrWidthInBytes = si.framebufferWidth * myFormat.bitsPerPixel / 8; - - char *scr = (image->data + y * scrWidthInBytes - + x * myFormat.bitsPerPixel / 8); - - for (h = 0; h < height; h++) { - memcpy(scr, buf, widthInBytes); - buf += widthInBytes; - scr += scrWidthInBytes; - } - } else { - CopyBGR233ToScreen((CARD8 *)buf, x, y, width, height); - } + for (h = 0; h < width; h++) { + if (bpp == 8) { + *(ucp+h) = (unsigned char) fill; + } else if (bpp == 16) { + *(usp+h) = (unsigned short) fill; + } else if (bpp == 24) { + *(ucp + 3*h + b0) = (unsigned char) ((fill & 0x0000ff) >> 0); + *(ucp + 3*h + b1) = (unsigned char) ((fill & 0x00ff00) >> 8); + *(ucp + 3*h + b2) = (unsigned char) ((fill & 0xff0000) >> 16); + } else if (bpp == 32) { + *(uip+h) = (unsigned int) fill; + } + } -#ifdef MITSHM - if (appData.useShm) { - XShmPutImage(dpy, desktopWin, gc, image, x, y, x, y, width, height, False); - return; - } + scr = im->data + y * Bpl + x * Bpp; + + for (h = 0; h < height; h++) { + memcpy(scr, buf, widthInBytes); + scr += Bpl; + } + put_image(x, y, x, y, width, height, 1); + maybe_sync(width, height); +} + +void copy_rect(int x, int y, int width, int height, int src_x, int src_y) { + char *src, *dst; + int i; + XImage *im = image_scale ? image_scale : image; + int Bpp = im->bits_per_pixel / 8; + int Bpl = im->bytes_per_line; + int did2 = 0; + +#if 0 +fprintf(stderr, "copy_rect: %04dx%04d+%04d+%04d -- %04d %04d Bpp=%d Bpl=%d\n", width, height, x, y, src_x, src_y, Bpp, Bpl); #endif - XPutImage(dpy, desktopWin, gc, image, x, y, x, y, width, height); + copyrect2: + + if (y < src_y) { + src = im->data + src_y * Bpl + src_x * Bpp; + dst = im->data + y * Bpl + x * Bpp; + for (i = 0; i < height; i++) { + memmove(dst, src, Bpp * width); + src += Bpl; + dst += Bpl; + } + } else { + src = im->data + (src_y + height - 1) * Bpl + src_x * Bpp; + dst = im->data + (y + height - 1) * Bpl + x * Bpp; + for (i = 0; i < height; i++) { + memmove(dst, src, Bpp * width); + src -= Bpl; + dst -= Bpl; + } + } + + if (image_scale && !did2) { + im = image; + Bpp = im->bits_per_pixel / 8; + Bpl = im->bytes_per_line; + + x *= scale_factor_x; + y *= scale_factor_y; + src_x *= scale_factor_x; + src_y *= scale_factor_y; + width = scale_round(width, scale_factor_x); + height = scale_round(height, scale_factor_y); + + did2 = 1; + goto copyrect2; + } +} + + +/* + * CopyDataToScreen. + */ + +void +CopyDataToScreen(char *buf, int x, int y, int width, int height) +{ + if (appData.chatOnly) { + return; + } + if (appData.rawDelay != 0) { + XFillRectangle(dpy, desktopWin, gc, x, y, width, height); + XSync(dpy,False); + usleep(appData.rawDelay * 1000); + } + + if (appData.useBGR233) { + CopyBGR233ToScreen((CARD8 *)buf, x, y, width, height); + } else if (appData.useBGR565) { + CopyBGR565ToScreen((CARD16 *)buf, x, y, width, height); + } else { + int h; + int widthInBytes = width * myFormat.bitsPerPixel / 8; + int scrWidthInBytes = si.framebufferWidth * myFormat.bitsPerPixel / 8; + char *scr; + XImage *im = image_scale ? image_scale : image; + + if (scrWidthInBytes != im->bytes_per_line) scrWidthInBytes = im->bytes_per_line; + + scr = (im->data + y * scrWidthInBytes + + x * myFormat.bitsPerPixel / 8); + + for (h = 0; h < height; h++) { + memcpy(scr, buf, widthInBytes); + buf += widthInBytes; + scr += scrWidthInBytes; + } + } + + put_image(x, y, x, y, width, height, 0); + maybe_sync(width, height); } @@ -401,62 +2755,338 @@ static void CopyBGR233ToScreen(CARD8 *buf, int x, int y, int width, int height) { - int p, q; - int xoff = 7 - (x & 7); - int xcur; - int fbwb = si.framebufferWidth / 8; - CARD8 *scr1 = ((CARD8 *)image->data) + y * fbwb + x / 8; - CARD8 *scrt; - CARD8 *scr8 = ((CARD8 *)image->data) + y * si.framebufferWidth + x; - CARD16 *scr16 = ((CARD16 *)image->data) + y * si.framebufferWidth + x; - CARD32 *scr32 = ((CARD32 *)image->data) + y * si.framebufferWidth + x; + XImage *im = image_scale ? image_scale : image; + int p, q; + int xoff = 7 - (x & 7); + int xcur; + int fbwb = si.framebufferWidth / 8; + int src_width8 = im->bytes_per_line/1; + int src_width16 = im->bytes_per_line/2; + int src_width32 = im->bytes_per_line/4; + CARD8 *src1 = ((CARD8 *)im->data) + y * fbwb + x / 8; + CARD8 *srct; + CARD8 *src8 = ( (CARD8 *)im->data) + y * src_width8 + x; + CARD16 *src16 = ((CARD16 *)im->data) + y * src_width16 + x; + CARD32 *src32 = ((CARD32 *)im->data) + y * src_width32 + x; + int b0, b1, b2; - switch (visbpp) { + switch (visbpp) { /* thanks to Chris Hooper for single bpp support */ - case 1: - for (q = 0; q < height; q++) { - xcur = xoff; - scrt = scr1; - for (p = 0; p < width; p++) { - *scrt = ((*scrt & ~(1 << xcur)) - | (BGR233ToPixel[*(buf++)] << xcur)); - - if (xcur-- == 0) { - xcur = 7; - scrt++; - } - } - scr1 += fbwb; - } - break; - - case 8: - for (q = 0; q < height; q++) { - for (p = 0; p < width; p++) { - *(scr8++) = BGR233ToPixel[*(buf++)]; - } - scr8 += si.framebufferWidth - width; - } - break; - - case 16: - for (q = 0; q < height; q++) { - for (p = 0; p < width; p++) { - *(scr16++) = BGR233ToPixel[*(buf++)]; - } - scr16 += si.framebufferWidth - width; - } - break; - - case 32: - for (q = 0; q < height; q++) { - for (p = 0; p < width; p++) { - *(scr32++) = BGR233ToPixel[*(buf++)]; - } - scr32 += si.framebufferWidth - width; - } - break; - } + case 1: + for (q = 0; q < height; q++) { + xcur = xoff; + srct = src1; + for (p = 0; p < width; p++) { + *srct = ((*srct & ~(1 << xcur)) + | (BGR233ToPixel[*(buf++)] << xcur)); + + if (xcur-- == 0) { + xcur = 7; + srct++; + } + } + src1 += fbwb; + } + break; + + case 8: + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(src8++) = BGR233ToPixel[*(buf++)]; + } + src8 += src_width8 - width; + } + break; + + case 16: + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(src16++) = BGR233ToPixel[*(buf++)]; + } + src16 += src_width16 - width; + } + break; + + case 24: + if (isLSB) { + b0 = 0; b1 = 1; b2 = 2; + } else { + b0 = 2; b1 = 1; b2 = 0; + } + src8 = ((CARD8 *)im->data) + (y * si.framebufferWidth + x) * 3; + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + CARD32 v = BGR233ToPixel[*(buf++)]; + *(src8 + b0) = (unsigned char) ((v & 0x0000ff) >> 0); + *(src8 + b1) = (unsigned char) ((v & 0x00ff00) >> 8); + *(src8 + b2) = (unsigned char) ((v & 0xff0000) >> 16); + src8 += 3; + } + src8 += (si.framebufferWidth - width) * 3; + } + break; + + case 32: + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(src32++) = BGR233ToPixel[*(buf++)]; + } + src32 += src_width32 - width; + } + break; + } +} + +static void +BGR565_24bpp(CARD16 *buf, int x, int y, int width, int height) +{ + int p, q; + int b0, b1, b2; + XImage *im = image_scale ? image_scale : image; + unsigned char *src= (unsigned char *)im->data + (y * si.framebufferWidth + x) * 3; + + if (isLSB) { + b0 = 0; b1 = 1; b2 = 2; + } else { + b0 = 2; b1 = 1; b2 = 0; + } + + /* case 24: */ + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + CARD32 v = BGR565ToPixel[*(buf++)]; + *(src + b0) = (unsigned char) ((v & 0x0000ff) >> 0); + *(src + b1) = (unsigned char) ((v & 0x00ff00) >> 8); + *(src + b2) = (unsigned char) ((v & 0xff0000) >> 16); + src += 3; + } + src += (si.framebufferWidth - width) * 3; + } +} + +static void +CopyBGR565ToScreen(CARD16 *buf, int x, int y, int width, int height) +{ + int p, q; + XImage *im = image_scale ? image_scale : image; + int src_width32 = im->bytes_per_line/4; + CARD32 *src32 = ((CARD32 *)im->data) + y * src_width32 + x; + + if (visbpp == 24) { + BGR565_24bpp(buf, x, y, width, height); + return; + } + + /* case 32: */ + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(src32++) = BGR565ToPixel[*(buf++)]; + } + src32 += src_width32 - width; + } +} + +static void reset_image(void) { + if (UsingShm()) { + ShmDetach(); + } + if (image && image->data) { + XDestroyImage(image); + fprintf(stderr, "reset_image: destroyed 'image'\n"); + } + image = NULL; + if (image_ycrop && image_ycrop->data) { + XDestroyImage(image_ycrop); + fprintf(stderr, "reset_image: destroyed 'image_ycrop'\n"); + } + image_ycrop = NULL; + if (image_scale && image_scale->data) { + XDestroyImage(image_scale); + fprintf(stderr, "reset_image: destroyed 'image_scale'\n"); + } + image_scale = NULL; + + if (UsingShm()) { + ShmCleanup(); + } + create_image(); + XFlush(dpy); +} + +void ReDoDesktop(void) { + int w, w0, h, h0, x, y, dw, dh; + int fs = 0; + int autoscale = 0; + Position x_orig, y_orig; + Dimension w_orig, h_orig; + + if (!appData.fullScreen && appData.scale != NULL && !strcmp(appData.scale, "auto")) { + autoscale = 1; + } + + fprintf(stderr, "ReDoDesktop: ycrop: %d\n", appData.yCrop); + + XtVaGetValues(toplevel, XtNx, &x_orig, XtNy, &y_orig, NULL); + XtVaGetValues(toplevel, XtNheight, &h_orig, XtNwidth, &w_orig, NULL); + + check_tall(); + + if (appData.yCrop) { + if (appData.yCrop < 0 || old_width <= 0) { + appData.yCrop = guessCrop(); + fprintf(stderr, "Set -ycrop to: %d\n", appData.yCrop); + } else { + int w1 = si.framebufferWidth; + appData.yCrop = (w1 * appData.yCrop) / old_width; + if (appData.yCrop <= 100) { + appData.yCrop = guessCrop(); + fprintf(stderr, "Set small -ycrop to: %d\n", appData.yCrop); + } + } + fprintf(stderr, "Using -ycrop: %d\n", appData.yCrop); + } + + old_width = si.framebufferWidth; + old_height = si.framebufferHeight; + + if (appData.fullScreen) { + if (prev_fb_width != si.framebufferWidth || prev_fb_height != si.framebufferHeight) { + int xmax = si.framebufferWidth; + int ymax = si.framebufferHeight; + if (appData.yCrop > 0) { + ymax = appData.yCrop; + } + if (scale_x > 0) { + xmax = scale_round(xmax, scale_factor_x); + ymax = scale_round(ymax, scale_factor_y); + } + if (xmax < dpyWidth || ymax < dpyHeight) { + FullScreenOff(); + fs = 1; + } + } + } + + prev_fb_width = si.framebufferWidth; + prev_fb_height = si.framebufferHeight; + + if (appData.fullScreen) { + + int xmax = si.framebufferWidth; + int ymax = si.framebufferHeight; + if (scale_x > 0) { + xmax = scale_round(xmax, scale_factor_x); + ymax = scale_round(ymax, scale_factor_y); + } + + if (image && image->data) { + int len; + int h = image->height; + int w = image->width; + len = image->bytes_per_line * image->height; + /* black out window first: */ + memset(image->data, 0, len); + XPutImage(dpy, XtWindow(desktop), gc, image, 0, 0, 0, 0, w, h); + XFlush(dpy); + } + + /* XXX scaling?? */ + XtResizeWidget(desktop, xmax, ymax, 0); + + XSync(dpy, False); + usleep(100*1000); + FullScreenOn(); + XSync(dpy, False); + usleep(100*1000); + reset_image(); + return; + } + + dw = appData.wmDecorationWidth; + dh = appData.wmDecorationHeight; + + w = si.framebufferWidth; + h = si.framebufferHeight; + w0 = w; + h0 = h; + if (appData.yCrop > 0) { + h = appData.yCrop; + } + if (image_scale) { + w = scale_round(w, scale_factor_x); + h = scale_round(h, scale_factor_y); + w0 = scale_round(w0, scale_factor_x); + h0 = scale_round(h0, scale_factor_y); + } + + if (w + dw >= dpyWidth) { + w = dpyWidth - dw; + } + if (h + dh >= dpyHeight) { + h = dpyHeight - dh; + } + + if (!autoscale) { + XtVaSetValues(toplevel, XtNmaxWidth, w, XtNmaxHeight, h, NULL); + } else { + XtVaSetValues(toplevel, XtNmaxWidth, dpyWidth, XtNmaxHeight, dpyHeight, NULL); + } + + XtVaSetValues(desktop, XtNwidth, w0, XtNheight, h0, NULL); + + XtResizeWidget(desktop, w0, h0, 0); + + if (appData.yCrop > 0) { + int ycrop = appData.yCrop; + if (image_scale) { + ycrop *= scale_factor_y; + } + XtVaSetValues(toplevel, XtNmaxHeight, ycrop, NULL); + XtVaSetValues(form, XtNmaxHeight, ycrop, NULL); + } + + x = (dpyWidth - w - dw)/2; + y = (dpyHeight - h - dh)/2; + + if (!autoscale) { + + if (!getenv("VNCVIEWER_ALWAYS_RECENTER")) { + int x_cm_old, y_cm_old; + int x_cm_new, y_cm_new; + int x_try, y_try; + + x_cm_old = (int) x_orig + ((int) w_orig)/2; + y_cm_old = (int) y_orig + ((int) h_orig)/2; + + x_cm_new = dpyWidth/2; + y_cm_new = dpyHeight/2; + + x_try = x + (x_cm_old - x_cm_new); + y_try = y + (y_cm_old - y_cm_new); + if (x_try < 0) { + x_try = 0; + } + if (y_try < 0) { + y_try = 0; + } + if (x_try + w + dw > dpyWidth) { + x_try = dpyWidth - w - dw; + } + if (y_try + h + dh > dpyHeight) { + y_try = dpyHeight - h - dh; + } + x = x_try; + y = y_try; + } + + XtConfigureWidget(toplevel, x + dw, y + dh, w, h, 0); + } + + reset_image(); + + if (fs) { + FullScreenOn(); + } } diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/dialogs.c vnc_unixsrc/vncviewer/dialogs.c --- vnc_unixsrc.orig/vncviewer/dialogs.c 2000-10-26 15:19:19.000000000 -0400 +++ vnc_unixsrc/vncviewer/dialogs.c 2010-02-25 22:33:06.000000000 -0500 @@ -25,75 +25,564 @@ #include <X11/Xaw/Dialog.h> static Bool serverDialogDone = False; +static Bool userDialogDone = False; static Bool passwordDialogDone = False; +static Bool ycropDialogDone = False; +static Bool scaleDialogDone = False; +static Bool escapeDialogDone = False; +static Bool scbarDialogDone = False; +static Bool scaleNDialogDone = False; +static Bool qualityDialogDone = False; +static Bool compressDialogDone = False; + +extern void popupFixer(Widget wid); + +int use_tty(void) { + if (appData.notty) { + return 0; + } else if (!isatty(0)) { + return 0; + } + return 1; +} + +void +ScaleDialogDone(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + scaleDialogDone = True; + if (w || event || params || num_params) {} +} + +void +EscapeDialogDone(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + escapeDialogDone = True; + if (w || event || params || num_params) {} +} + +void dialog_over(Widget wid) { + if (appData.fullScreen) { + if (!net_wm_supported()) { + XtVaSetValues(wid, XtNoverrideRedirect, True, NULL); + XSync(dpy, True); + } + } +} + +extern int XError_ign; + +void dialog_input(Widget wid) { + XError_ign = 1; + XSetInputFocus(dpy, XtWindow(wid), RevertToParent, CurrentTime); + XSync(dpy, False); + usleep(30 * 1000); + XSync(dpy, False); + usleep(20 * 1000); + XSync(dpy, False); + XError_ign = 0; +} + +static void rmNL(char *s) { + int len; + if (s == NULL) { + return; + } + len = strlen(s); + if (len > 0 && s[len-1] == '\n') { + s[len-1] = '\0'; + } +} + +static void wm_delete(Widget w, char *func) { + char str[1024]; + Atom wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + XSetWMProtocols(dpy, XtWindow(w), &wmDeleteWindow, 1); + if (func) { + sprintf(str, "<Message>WM_PROTOCOLS: %s", func); + XtOverrideTranslations(w, XtParseTranslationTable (str)); + } +} + +static void xtmove(Widget w) { + XtMoveWidget(w, WidthOfScreen(XtScreen(w))*2/5, HeightOfScreen(XtScreen(w))*2/5); +} + +char * +DoScaleDialog() +{ + Widget pshell, dialog; + char *scaleValue; + char *valueString; + + pshell = XtVaCreatePopupShell("scaleDialog", transientShellWidgetClass, + toplevel, NULL); + dialog = XtVaCreateManagedWidget("dialog", dialogWidgetClass, pshell, NULL); + + dialog_over(pshell); + + if (0) XtMoveWidget(pshell, WidthOfScreen(XtScreen(pshell))*2/5, HeightOfScreen(XtScreen(pshell))*2/5); + XtPopup(pshell, XtGrabNonexclusive); + XtRealizeWidget(pshell); + + if (appData.scale != NULL) { + String label; + char tmp[410]; + XtVaGetValues(dialog, XtNlabel, &label, NULL); + if (strlen(label) + strlen(appData.scale) < 400) { + sprintf(tmp, "%s %s", label, appData.scale); + XtVaSetValues(dialog, XtNlabel, tmp, NULL); + } + } + + + if (1 && appData.popupFix) { + popupFixer(pshell); + } else { + xtmove(pshell); + } + dialog_input(pshell); + wm_delete(pshell, "ScaleDialogDone()"); + + scaleDialogDone = False; + + while (!scaleDialogDone) { + XtAppProcessEvent(appContext, XtIMAll); + } + + valueString = XawDialogGetValueString(dialog); + rmNL(valueString); + scaleValue = XtNewString(valueString); + + XtPopdown(pshell); + return scaleValue; +} + +char * +DoEscapeKeysDialog() +{ + Widget pshell, dialog; + char *escapeValue; + char *valueString; + char *curr = appData.escapeKeys ? appData.escapeKeys : "default"; + + pshell = XtVaCreatePopupShell("escapeDialog", transientShellWidgetClass, + toplevel, NULL); + dialog = XtVaCreateManagedWidget("dialog", dialogWidgetClass, pshell, NULL); + + dialog_over(pshell); + + if(0) XtMoveWidget(pshell, WidthOfScreen(XtScreen(pshell))*2/5, HeightOfScreen(XtScreen(pshell))*2/5); + XtPopup(pshell, XtGrabNonexclusive); + XtRealizeWidget(pshell); + + if (curr != NULL) { + String label; + char tmp[3010]; + XtVaGetValues(dialog, XtNlabel, &label, NULL); + if (strlen(label) + strlen(curr) < 3000) { + sprintf(tmp, "%s %s", label, curr); + XtVaSetValues(dialog, XtNlabel, tmp, NULL); + } + } + + if (appData.popupFix) { + popupFixer(pshell); + } else { + /* too big */ + if (0) xtmove(pshell); + } + dialog_input(pshell); + wm_delete(pshell, "EscapeDialogDone()"); + + escapeDialogDone = False; + + while (!escapeDialogDone) { + XtAppProcessEvent(appContext, XtIMAll); + } + + valueString = XawDialogGetValueString(dialog); + rmNL(valueString); + escapeValue = XtNewString(valueString); + + XtPopdown(pshell); + return escapeValue; +} + +void +YCropDialogDone(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + ycropDialogDone = True; + if (w || event || params || num_params) {} +} + +char * +DoYCropDialog() +{ + Widget pshell, dialog; + char *ycropValue; + char *valueString; + + pshell = XtVaCreatePopupShell("ycropDialog", transientShellWidgetClass, + toplevel, NULL); + dialog = XtVaCreateManagedWidget("dialog", dialogWidgetClass, pshell, NULL); + + dialog_over(pshell); + + if(0) XtMoveWidget(pshell, WidthOfScreen(XtScreen(pshell))*2/5, HeightOfScreen(XtScreen(pshell))*2/5); + XtPopup(pshell, XtGrabNonexclusive); + XtRealizeWidget(pshell); + + if (1 && appData.popupFix) { + popupFixer(pshell); + } else { + xtmove(pshell); + } + dialog_input(pshell); + wm_delete(pshell, "YCropDialogDone()"); + + ycropDialogDone = False; + + while (!ycropDialogDone) { + XtAppProcessEvent(appContext, XtIMAll); + } + + valueString = XawDialogGetValueString(dialog); + rmNL(valueString); + ycropValue = XtNewString(valueString); + + XtPopdown(pshell); + return ycropValue; +} + +void +ScbarDialogDone(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + scbarDialogDone = True; + if (w || event || params || num_params) {} +} + +char * +DoScbarDialog() +{ + Widget pshell, dialog; + char *scbarValue; + char *valueString; + + pshell = XtVaCreatePopupShell("scbarDialog", transientShellWidgetClass, + toplevel, NULL); + dialog = XtVaCreateManagedWidget("dialog", dialogWidgetClass, pshell, NULL); + + dialog_over(pshell); + + if(0) XtMoveWidget(pshell, WidthOfScreen(XtScreen(pshell))*2/5, HeightOfScreen(XtScreen(pshell))*2/5); + XtPopup(pshell, XtGrabNonexclusive); + XtRealizeWidget(pshell); + + if (1 && appData.popupFix) { + popupFixer(pshell); + } else { + xtmove(pshell); + } + dialog_input(pshell); + wm_delete(pshell, "ScbarDialogDone()"); + + scbarDialogDone = False; + + while (!scbarDialogDone) { + XtAppProcessEvent(appContext, XtIMAll); + } + + valueString = XawDialogGetValueString(dialog); + rmNL(valueString); + scbarValue = XtNewString(valueString); + + XtPopdown(pshell); + return scbarValue; +} + +void +ScaleNDialogDone(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + scaleNDialogDone = True; + if (w || event || params || num_params) {} +} + +char * +DoScaleNDialog() +{ + Widget pshell, dialog; + char *scaleNValue; + char *valueString; + + pshell = XtVaCreatePopupShell("scaleNDialog", transientShellWidgetClass, + toplevel, NULL); + dialog = XtVaCreateManagedWidget("dialog", dialogWidgetClass, pshell, NULL); + + dialog_over(pshell); + wm_delete(pshell, "ScaleNDialogDone()"); + + if(0) XtMoveWidget(pshell, WidthOfScreen(XtScreen(pshell))*2/5, HeightOfScreen(XtScreen(pshell))*2/5); + XtPopup(pshell, XtGrabNonexclusive); + XtRealizeWidget(pshell); + + if (appData.popupFix) { + popupFixer(pshell); + } else { + xtmove(pshell); + } + dialog_input(pshell); + wm_delete(pshell, "ScaleNDialogDone()"); + + scaleNDialogDone = False; + + while (!scaleNDialogDone) { + XtAppProcessEvent(appContext, XtIMAll); + } + + valueString = XawDialogGetValueString(dialog); + rmNL(valueString); + scaleNValue = XtNewString(valueString); + + XtPopdown(pshell); + return scaleNValue; +} + +void +QualityDialogDone(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + qualityDialogDone = True; + if (w || event || params || num_params) {} +} + +char * +DoQualityDialog() +{ + Widget pshell, dialog; + char *qualityValue; + char *valueString; + + pshell = XtVaCreatePopupShell("qualityDialog", transientShellWidgetClass, + toplevel, NULL); + dialog = XtVaCreateManagedWidget("dialog", dialogWidgetClass, pshell, NULL); + + dialog_over(pshell); + + if(0) XtMoveWidget(pshell, WidthOfScreen(XtScreen(pshell))*2/5, HeightOfScreen(XtScreen(pshell))*2/5); + XtPopup(pshell, XtGrabNonexclusive); + XtRealizeWidget(pshell); + + if (1 && appData.popupFix) { + popupFixer(pshell); + } else { + xtmove(pshell); + } + dialog_input(pshell); + wm_delete(pshell, "QualityDialogDone() HideQuality()"); + + qualityDialogDone = False; + + while (!qualityDialogDone) { + XtAppProcessEvent(appContext, XtIMAll); + } + + valueString = XawDialogGetValueString(dialog); + rmNL(valueString); + qualityValue = XtNewString(valueString); + + XtPopdown(pshell); + return qualityValue; +} + +void +CompressDialogDone(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + compressDialogDone = True; + if (w || event || params || num_params) {} +} + +char * +DoCompressDialog() +{ + Widget pshell, dialog; + char *compressValue; + char *valueString; + + fprintf(stderr, "compress start:\n"); + + pshell = XtVaCreatePopupShell("compressDialog", transientShellWidgetClass, + toplevel, NULL); + dialog = XtVaCreateManagedWidget("dialog", dialogWidgetClass, pshell, NULL); + + dialog_over(pshell); + + if(0) XtMoveWidget(pshell, WidthOfScreen(XtScreen(pshell))*2/5, HeightOfScreen(XtScreen(pshell))*2/5); + XtPopup(pshell, XtGrabNonexclusive); + XtRealizeWidget(pshell); + + if (1 && appData.popupFix) { + popupFixer(pshell); + } else { + xtmove(pshell); + } + dialog_input(pshell); + wm_delete(pshell, "CompressDialogDone() HideCompress()"); + + compressDialogDone = False; + + while (!compressDialogDone) { + XtAppProcessEvent(appContext, XtIMAll); + } + + valueString = XawDialogGetValueString(dialog); + rmNL(valueString); + compressValue = XtNewString(valueString); + + fprintf(stderr, "compress done: %s\n", compressValue); + + XtPopdown(pshell); + return compressValue; +} void ServerDialogDone(Widget w, XEvent *event, String *params, Cardinal *num_params) { - serverDialogDone = True; + serverDialogDone = True; + if (w || event || params || num_params) {} } char * DoServerDialog() { - Widget pshell, dialog; - char *vncServerName; - char *valueString; + Widget pshell, dialog; + char *vncServerName; + char *valueString; - pshell = XtVaCreatePopupShell("serverDialog", transientShellWidgetClass, + pshell = XtVaCreatePopupShell("serverDialog", transientShellWidgetClass, toplevel, NULL); - dialog = XtVaCreateManagedWidget("dialog", dialogWidgetClass, pshell, NULL); + dialog = XtVaCreateManagedWidget("dialog", dialogWidgetClass, pshell, NULL); - XtMoveWidget(pshell, WidthOfScreen(XtScreen(pshell))*2/5, - HeightOfScreen(XtScreen(pshell))*2/5); - XtPopup(pshell, XtGrabNonexclusive); - XtRealizeWidget(pshell); + dialog_over(pshell); - serverDialogDone = False; + if (0) XtMoveWidget(pshell, WidthOfScreen(XtScreen(pshell))*2/5, HeightOfScreen(XtScreen(pshell))*2/5); + XtPopup(pshell, XtGrabNonexclusive); + XtRealizeWidget(pshell); + + if (0 && appData.popupFix) { + popupFixer(pshell); + } else { + xtmove(pshell); + } +#if 0 + dialog_input(pshell); +#endif + wm_delete(pshell, "ServerDialogDone()"); + + serverDialogDone = False; + + while (!serverDialogDone) { + XtAppProcessEvent(appContext, XtIMAll); + } + + valueString = XawDialogGetValueString(dialog); + rmNL(valueString); + vncServerName = XtNewString(valueString); - while (!serverDialogDone) { - XtAppProcessEvent(appContext, XtIMAll); - } + XtPopdown(pshell); + return vncServerName; +} - valueString = XawDialogGetValueString(dialog); - vncServerName = XtNewString(valueString); +void +UserDialogDone(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + userDialogDone = True; + if (w || event || params || num_params) {} +} - XtPopdown(pshell); - return vncServerName; +char * +DoUserDialog() +{ + Widget pshell, dialog; + char *userName; + char *valueString; + + pshell = XtVaCreatePopupShell("userDialog", transientShellWidgetClass, + toplevel, NULL); + dialog = XtVaCreateManagedWidget("dialog", dialogWidgetClass, pshell, NULL); + + dialog_over(pshell); + + if(0) XtMoveWidget(pshell, WidthOfScreen(XtScreen(pshell))*2/5, HeightOfScreen(XtScreen(pshell))*2/5); + XtPopup(pshell, XtGrabNonexclusive); + XtRealizeWidget(pshell); + + if (0 && appData.popupFix) { + popupFixer(pshell); + } else { + xtmove(pshell); + } +#if 0 + dialog_input(pshell); +#endif + wm_delete(pshell, "UserDialogDone()"); + + userDialogDone = False; + + while (!userDialogDone) { + XtAppProcessEvent(appContext, XtIMAll); + } + + valueString = XawDialogGetValueString(dialog); + rmNL(valueString); + userName = XtNewString(valueString); + + XtPopdown(pshell); + return userName; } void -PasswordDialogDone(Widget w, XEvent *event, String *params, - Cardinal *num_params) +PasswordDialogDone(Widget w, XEvent *event, String *params, Cardinal *num_params) { - passwordDialogDone = True; + passwordDialogDone = True; + if (w || event || params || num_params) {} } char * DoPasswordDialog() { - Widget pshell, dialog; - char *password; - char *valueString; + Widget pshell, dialog; + char *password; + char *valueString; - pshell = XtVaCreatePopupShell("passwordDialog", transientShellWidgetClass, + pshell = XtVaCreatePopupShell("passwordDialog", transientShellWidgetClass, toplevel, NULL); - dialog = XtVaCreateManagedWidget("dialog", dialogWidgetClass, pshell, NULL); - - XtMoveWidget(pshell, WidthOfScreen(XtScreen(pshell))*2/5, - HeightOfScreen(XtScreen(pshell))*2/5); - XtPopup(pshell, XtGrabNonexclusive); - XtRealizeWidget(pshell); - - passwordDialogDone = False; + dialog = XtVaCreateManagedWidget("dialog", dialogWidgetClass, pshell, NULL); - while (!passwordDialogDone) { - XtAppProcessEvent(appContext, XtIMAll); - } + dialog_over(pshell); - valueString = XawDialogGetValueString(dialog); - password = XtNewString(valueString); + if(0) XtMoveWidget(pshell, WidthOfScreen(XtScreen(pshell))*2/5, HeightOfScreen(XtScreen(pshell))*2/5); + XtPopup(pshell, XtGrabNonexclusive); + XtRealizeWidget(pshell); + + if (0 && appData.popupFix) { + popupFixer(pshell); + } else { + xtmove(pshell); + } +#if 0 + dialog_input(pshell); +#endif + wm_delete(pshell, "PasswordDialogDone()"); + + passwordDialogDone = False; + + while (!passwordDialogDone) { + XtAppProcessEvent(appContext, XtIMAll); + } + + valueString = XawDialogGetValueString(dialog); + rmNL(valueString); + password = XtNewString(valueString); - XtPopdown(pshell); - return password; + XtPopdown(pshell); + return password; } diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/fullscreen.c vnc_unixsrc/vncviewer/fullscreen.c --- vnc_unixsrc.orig/vncviewer/fullscreen.c 2003-10-09 05:23:49.000000000 -0400 +++ vnc_unixsrc/vncviewer/fullscreen.c 2010-02-25 22:37:49.000000000 -0500 @@ -22,20 +22,24 @@ */ #include <vncviewer.h> +#include <time.h> #include <X11/Xaw/Form.h> #include <X11/Xaw/Viewport.h> #include <X11/Xaw/Toggle.h> static Bool DoBumpScroll(); +static Bool DoJumpScroll(); static void BumpScrollTimerCallback(XtPointer clientData, XtIntervalId *id); +static void JumpScrollTimerCallback(XtPointer clientData, XtIntervalId *id); static XtIntervalId timer; static Bool timerSet = False; static Bool scrollLeft, scrollRight, scrollUp, scrollDown; -static Position desktopX, desktopY; +Position desktopX, desktopY; static Dimension viewportWidth, viewportHeight; static Dimension scrollbarWidth, scrollbarHeight; +int scale_round(int len, double fac); /* * FullScreenOn goes into full-screen mode. It makes the toplevel window @@ -78,112 +82,456 @@ * variables so that FullScreenOff can use them. */ -void -FullScreenOn() -{ - Dimension toplevelWidth, toplevelHeight; - Dimension oldViewportWidth, oldViewportHeight, clipWidth, clipHeight; - Position viewportX, viewportY; - - appData.fullScreen = True; - - if (si.framebufferWidth > dpyWidth || si.framebufferHeight > dpyHeight) { - - XtVaSetValues(viewport, XtNforceBars, True, NULL); - XtVaGetValues(viewport, XtNwidth, &oldViewportWidth, - XtNheight, &oldViewportHeight, NULL); - XtVaGetValues(XtNameToWidget(viewport, "clip"), - XtNwidth, &clipWidth, XtNheight, &clipHeight, NULL); - - scrollbarWidth = oldViewportWidth - clipWidth; - scrollbarHeight = oldViewportHeight - clipHeight; - - if (si.framebufferWidth > dpyWidth) { - viewportWidth = toplevelWidth = dpyWidth + scrollbarWidth; - } else { - viewportWidth = si.framebufferWidth + scrollbarWidth; - toplevelWidth = dpyWidth; - } - - if (si.framebufferHeight > dpyHeight) { - viewportHeight = toplevelHeight = dpyHeight + scrollbarHeight; - } else { - viewportHeight = si.framebufferHeight + scrollbarHeight; - toplevelHeight = dpyHeight; - } - - } else { - viewportWidth = si.framebufferWidth; - viewportHeight = si.framebufferHeight; - toplevelWidth = dpyWidth; - toplevelHeight = dpyHeight; - } +int net_wm_supported(void) { + unsigned char *data; + unsigned long items_read, items_left, i; + int ret, format; + Window wm; + Atom type; + Atom _NET_SUPPORTING_WM_CHECK; + Atom _NET_SUPPORTED; + Atom _NET_WM_STATE; + Atom _NET_WM_STATE_FULLSCREEN; + + static time_t last_check = 0; + static int fs_supported = -1; + + if (fs_supported >= 0 && time(NULL) < last_check + 600) { + static int first = 1; + if (first) { + fprintf(stderr, "fs_supported: %d\n", fs_supported); + } + first = 0; + return fs_supported; + } + last_check = time(NULL); + + fs_supported = 0; + + _NET_SUPPORTING_WM_CHECK = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + _NET_SUPPORTED = XInternAtom(dpy, "_NET_SUPPORTED", False); + _NET_WM_STATE = XInternAtom(dpy, "_NET_WM_STATE", False); + _NET_WM_STATE_FULLSCREEN = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + + ret = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _NET_SUPPORTING_WM_CHECK, + 0L, 1L, False, XA_WINDOW, &type, &format, &items_read, &items_left, &data); + + if (ret != Success || !items_read) { + if (ret == Success) { + XFree(data); + } + return fs_supported; + } + + wm = ((Window*) data)[0]; + XFree(data); + + ret = XGetWindowProperty(dpy, wm, _NET_SUPPORTING_WM_CHECK, + 0L, 1L, False, XA_WINDOW, &type, &format, &items_read, &items_left, &data); + + if (ret != Success || !items_read) { + if (ret == Success) { + XFree(data); + } + return fs_supported; + } + + if (wm != ((Window*) data)[0]) { + XFree(data); + return fs_supported; + } + + ret = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _NET_SUPPORTED, + 0L, 8192L, False, XA_ATOM, &type, &format, &items_read, &items_left, &data); + + if (ret != Success || !items_read) { + if (ret == Success) { + XFree(data); + } + return fs_supported; + } + + for (i=0; i < items_read; i++) { + if ( ((Atom*) data)[i] == _NET_WM_STATE_FULLSCREEN) { + fs_supported = 1; + } + } + XFree(data); - viewportX = (toplevelWidth - viewportWidth) / 2; - viewportY = (toplevelHeight - viewportHeight) / 2; + return fs_supported; +} +static void net_wm_fullscreen(int to_fs) { + + int _NET_WM_STATE_REMOVE = 0; + int _NET_WM_STATE_ADD = 1; +#if 0 + int _NET_WM_STATE_TOGGLE = 2; +#endif + Atom _NET_WM_STATE = XInternAtom(dpy, "_NET_WM_STATE", False); + Atom _NET_WM_STATE_FULLSCREEN = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + XEvent xev; + + if (to_fs == 2) { + XChangeProperty(dpy, XtWindow(toplevel), _NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char*)&_NET_WM_STATE_FULLSCREEN, 1); + } else { + xev.xclient.type = ClientMessage; + xev.xclient.window = XtWindow(toplevel); + xev.xclient.message_type = _NET_WM_STATE; + xev.xclient.serial = 0; + xev.xclient.display = dpy; + xev.xclient.send_event = True; + xev.xclient.format = 32; + xev.xclient.data.l[0] = to_fs ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + xev.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN; + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + XSendEvent(dpy, DefaultRootWindow(dpy), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + } - /* We want to stop the window manager from managing our toplevel window. - This is not really a nice thing to do, so may not work properly with every - window manager. We do this simply by setting overrideRedirect and - reparenting our window to the root. The window manager will get a - ReparentNotify and hopefully clean up its frame window. */ + XSync(dpy, False); +} - XtVaSetValues(toplevel, XtNoverrideRedirect, True, NULL); +time_t main_grab = 0; - XReparentWindow(dpy, XtWindow(toplevel), DefaultRootWindow(dpy), 0, 0); +void fs_ungrab(int check) { + if (check) { + if (time(NULL) <= main_grab + 2) { + return; + } + if (net_wm_supported()) { + return; + } + } + fprintf(stderr, "calling fs_ungrab()\n"); + if (appData.grabAll) { /* runge top of FullScreenOff */ + fprintf(stderr, "calling XUngrabServer(dpy)\n"); + XUngrabServer(dpy); + } + if (appData.grabKeyboard) { + fprintf(stderr, "calling XUngrabKeyboard(dpy)\n"); + XtUngrabKeyboard(desktop, CurrentTime); + } +} - /* Some WMs does not obey x,y values of XReparentWindow; the window - is not placed in the upper, left corner. The code below fixes - this: It manually moves the window, after the Xserver is done - with XReparentWindow. The last XSync seems to prevent losing - focus, but I don't know why. */ - XSync(dpy, False); - XMoveWindow(dpy, XtWindow(toplevel), 0, 0); - XSync(dpy, False); - - /* Now we want to fix the size of "viewport". We shouldn't just change it - directly. Instead we set "toplevel" to the required size (which should - propagate through "form" to "viewport"). Then we remove "viewport" from - being managed by "form", change its resources to position it and make sure - that "form" won't attempt to resize it, then ask "form" to manage it - again. */ - - XtResizeWidget(toplevel, viewportWidth, viewportHeight, 0); - - XtUnmanageChild(viewport); - - XtVaSetValues(viewport, - XtNhorizDistance, viewportX, - XtNvertDistance, viewportY, - XtNleft, XtChainLeft, - XtNright, XtChainLeft, - XtNtop, XtChainTop, - XtNbottom, XtChainTop, - NULL); +void fs_grab(int check) { + if (check) { + if (time(NULL) <= main_grab + 2) { + return; + } + if (net_wm_supported()) { + return; + } + } + + main_grab = time(NULL); + + fprintf(stderr, "calling fs_grab()\n"); + +#define FORCE_UP \ + XSync(dpy, False); \ + XUnmapWindow(dpy, XtWindow(toplevel)); \ + XSync(dpy, False); \ + XMapWindow(dpy, XtWindow(toplevel)); \ + XRaiseWindow(dpy, XtWindow(toplevel)); \ + XSync(dpy, False); + + if (appData.grabKeyboard && XtGrabKeyboard(desktop, True, GrabModeAsync, GrabModeAsync, CurrentTime) != GrabSuccess) { + fprintf(stderr, "XtGrabKeyboard() failed.\n"); + XSync(dpy, False); + usleep(100 * 1000); + FORCE_UP + + if (XtGrabKeyboard(desktop, True, GrabModeAsync, GrabModeAsync, CurrentTime) != GrabSuccess) { + fprintf(stderr, "XtGrabKeyboard() failed again.\n"); + usleep(200 * 1000); + XSync(dpy, False); + if (XtGrabKeyboard(desktop, True, GrabModeAsync, GrabModeAsync, CurrentTime) != GrabSuccess) { + fprintf(stderr, "XtGrabKeyboard() failed 3rd time.\n"); + } else { + fprintf(stderr, "XtGrabKeyboard() OK 3rd try.\n"); + } + } else { + fprintf(stderr, "XtGrabKeyboard() OK 2nd try.\n"); + } + XRaiseWindow(dpy, XtWindow(toplevel)); + } + + if (appData.grabAll) { + fprintf(stderr, "calling XGrabServer(dpy)\n"); + if (! XGrabServer(dpy)) { + XSync(dpy, False); + usleep(100 * 1000); + fprintf(stderr, "calling XGrabServer(dpy) 2nd time\n"); + if (!XGrabServer(dpy)) { + XSync(dpy, False); + usleep(200 * 1000); + fprintf(stderr, "calling XGrabServer(dpy) 3rd time\n"); + if (XGrabServer(dpy)) { + fprintf(stderr, "XGrabServer(dpy) OK 3rd time\n"); + } + } else { + fprintf(stderr, "XGrabServer(dpy) OK 2nd time\n"); + } + XSync(dpy, False); + } + if (getenv("VNCVIEWER_FORCE_UP")) { + fprintf(stderr, "FORCE_UP\n"); + FORCE_UP + } + } +} + +extern int fullscreen_startup; +extern double last_fullscreen; + +#define set_size_hints() \ +{ \ + long supplied; \ + XSizeHints *sizehints = XAllocSizeHints(); \ + XGetWMSizeHints(dpy, topwin, sizehints, &supplied, XA_WM_NORMAL_HINTS); \ + if (sizehints->base_width < toplevelWidth) { \ + sizehints->base_width = toplevelWidth; \ + } \ + if (sizehints->base_height < toplevelHeight) { \ + sizehints->base_height = toplevelHeight; \ + } \ + if (sizehints->max_width < toplevelWidth) { \ + sizehints->max_width = toplevelWidth; \ + } \ + if (sizehints->max_height < toplevelHeight) { \ + sizehints->max_height = toplevelHeight; \ + } \ + XSetWMSizeHints(dpy, topwin, sizehints, XA_WM_NORMAL_HINTS); \ + XFree(sizehints); \ +} - XtManageChild(viewport); +extern int scale_x, scale_y; +extern double scale_factor_y; + +void +FullScreenOn() +{ + Dimension toplevelWidth, toplevelHeight; + Dimension oldViewportWidth, oldViewportHeight, clipWidth, clipHeight; + Position viewportX, viewportY; + int do_net_wm = net_wm_supported(); + int fbW = si.framebufferWidth; + int fbH = si.framebufferHeight; + int eff_height; + + Bool fsAlready = appData.fullScreen, toobig = False; + Window topwin = XtWindow(toplevel); + + appData.fullScreen = True; + + last_fullscreen = dnow(); + + if (scale_x > 0) { + fbW = scale_x; + fbH = scale_y; + } + + eff_height = fbH; + if (appData.yCrop > 0) { + eff_height = appData.yCrop; + if (scale_y > 0) { + eff_height = scale_round(eff_height, scale_factor_y); + } + } + + if (fbW > dpyWidth || eff_height > dpyHeight) { + + toobig = True; + + /* + * This is a crazy thing to have the scrollbars hang + * just a bit offscreen to the right and below. the user + * will not see them and bumpscroll will work. + */ + + XtVaSetValues(viewport, XtNforceBars, True, NULL); + XtVaGetValues(viewport, XtNwidth, &oldViewportWidth, XtNheight, &oldViewportHeight, NULL); + XtVaGetValues(XtNameToWidget(viewport, "clip"), XtNwidth, &clipWidth, XtNheight, &clipHeight, NULL); + + scrollbarWidth = oldViewportWidth - clipWidth; + scrollbarHeight = oldViewportHeight - clipHeight; + + if (fbW > dpyWidth) { + viewportWidth = toplevelWidth = dpyWidth + scrollbarWidth; + } else { + viewportWidth = fbW + scrollbarWidth; + toplevelWidth = dpyWidth; + } + + if (eff_height > dpyHeight) { + viewportHeight = toplevelHeight = dpyHeight + scrollbarHeight; + } else { + viewportHeight = eff_height + scrollbarHeight; + toplevelHeight = dpyHeight; + } + if (do_net_wm) { + /* but for _NET_WM we make toplevel be correct dpy size */ + toplevelWidth = dpyWidth; + toplevelHeight = dpyHeight; + } + + } else { + viewportWidth = fbW; + viewportHeight = eff_height; + toplevelWidth = dpyWidth; + toplevelHeight = dpyHeight; + } - /* Now we can set "toplevel" to its proper size. */ + viewportX = (toplevelWidth - viewportWidth) / 2; + viewportY = (toplevelHeight - viewportHeight) / 2; - XtResizeWidget(toplevel, toplevelWidth, toplevelHeight, 0); + if (viewportX < 0) viewportX = 0; + if (viewportY < 0) viewportY = 0; - /* Set the popup to overrideRedirect too */ - XtVaSetValues(popup, XtNoverrideRedirect, True, NULL); + /* We want to stop the window manager from managing our toplevel window. + This is not really a nice thing to do, so may not work properly with every + window manager. We do this simply by setting overrideRedirect and + reparenting our window to the root. The window manager will get a + ReparentNotify and hopefully clean up its frame window. */ - /* Try to get the input focus. */ + if (! fsAlready) { + if (!do_net_wm) { + /* added to try to raise it on top for some cirumstances */ + XUnmapWindow(dpy, topwin); + + XtVaSetValues(toplevel, XtNoverrideRedirect, True, NULL); +#if 0 + XtVaSetValues(viewport, XtNoverrideRedirect, True, NULL); + XtVaSetValues(desktop, XtNoverrideRedirect, True, NULL); +#endif + XtVaSetValues(popup, XtNoverrideRedirect, True, NULL); + + XReparentWindow(dpy, topwin, DefaultRootWindow(dpy), 0, 0); + + /* Some WMs does not obey x,y values of XReparentWindow; the window + is not placed in the upper, left corner. The code below fixes + this: It manually moves the window, after the Xserver is done + with XReparentWindow. The last XSync seems to prevent losing + focus, but I don't know why. */ + + XSync(dpy, False); + + /* added to try to raise it on top for some cirumstances */ + XMapRaised(dpy, topwin); + + XMoveWindow(dpy, topwin, 0, 0); + XSync(dpy, False); + } + + /* Now we want to fix the size of "viewport". We shouldn't just change it + directly. Instead we set "toplevel" to the required size (which should + propagate through "form" to "viewport"). Then we remove "viewport" from + being managed by "form", change its resources to position it and make sure + that "form" won't attempt to resize it, then ask "form" to manage it + again. */ + + XtResizeWidget(toplevel, viewportWidth, viewportHeight, 0); + + XtUnmanageChild(viewport); + + XtVaSetValues(viewport, + XtNhorizDistance, viewportX, + XtNvertDistance, viewportY, + XtNleft, XtChainLeft, + XtNright, XtChainLeft, + XtNtop, XtChainTop, + XtNbottom, XtChainTop, + NULL); + + XtManageChild(viewport); + XSync(dpy, False); + } else { + XSync(dpy, False); + } + + /* Now we can set "toplevel" to its proper size. */ + +#if 0 + XtVaSetValues(toplevel, XtNwidth, toplevelWidth, XtNheight, toplevelHeight, NULL); + XtResizeWidget(toplevel, toplevelWidth, toplevelHeight, 0); +#endif + XResizeWindow(dpy, topwin, toplevelWidth, toplevelHeight); + + if (do_net_wm) { + XWindowAttributes attr; + int ok = 0, i, delay = 20; + + usleep(delay * 1000); + +#define GSIZE() \ + XGetWindowAttributes(dpy, topwin, &attr); + +#define PSIZE(s) \ + XSync(dpy, False); \ + XGetWindowAttributes(dpy, topwin, &attr); \ + fprintf(stderr, "%s %dx%d+%d+%d\n", s, attr.width, attr.height, attr.x, attr.y); + + PSIZE("size-A:"); + + set_size_hints(); + + net_wm_fullscreen(1); + + PSIZE("size-B:"); + + for (i=0; i < 30; i++) { + usleep(delay * 1000); + GSIZE(); + fprintf(stderr, "size[%d] %dx%d+%d+%d\n", i, attr.width, attr.height, attr.x, attr.y); + if (attr.width == toplevelWidth && attr.height == toplevelHeight) { + ok = 1; + fprintf(stderr, "size ok.\n"); + XSync(dpy, False); + break; + } + set_size_hints(); + XResizeWindow(dpy, topwin, toplevelWidth, toplevelHeight); + XMoveWindow(dpy, topwin, 0, 0); + XSync(dpy, False); + } + + PSIZE("size-C:"); + } + + fprintf(stderr, "\ntoplevel: %dx%d viewport: %dx%d\n", toplevelWidth, toplevelHeight, viewportWidth, viewportHeight); + +#if defined (__SVR4) && defined (__sun) + if (!do_net_wm) { + /* CDE */ + XSync(dpy, False); + usleep(200 * 1000); + XMoveWindow(dpy, topwin, 0, 0); + XMapRaised(dpy, topwin); + XSync(dpy, False); + } +#endif + + if (fsAlready) { + XtResizeWidget(viewport, viewportWidth, viewportHeight, 0); + if (! toobig) { + XtVaSetValues(viewport, XtNforceBars, False, NULL); + } + XMoveWindow(dpy, topwin, viewportX, viewportY); + XSync(dpy, False); + } + + /* Try to get the input focus. */ - XSetInputFocus(dpy, DefaultRootWindow(dpy), RevertToPointerRoot, - CurrentTime); + /* original vnc: DefaultRootWindow(dpy) instead of PointerRoot */ + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); - /* Optionally, grab the keyboard. */ + /* Optionally, grab the keyboard. */ + fs_grab(0); - if (appData.grabKeyboard && - XtGrabKeyboard(desktop, True, GrabModeAsync, - GrabModeAsync, CurrentTime) != GrabSuccess) { - fprintf(stderr, "XtGrabKeyboard() failed.\n"); - } + /* finally done. */ } @@ -205,28 +553,52 @@ void FullScreenOff() { - int toplevelWidth = si.framebufferWidth; - int toplevelHeight = si.framebufferHeight; - - appData.fullScreen = False; + int toplevelWidth, toplevelHeight; + int do_net_wm = net_wm_supported(); + int fbW = si.framebufferWidth; + int fbH = si.framebufferHeight; + int eff_height; + + appData.fullScreen = False; + + last_fullscreen = dnow(); + + if (scale_x > 0) { + fbW = scale_x; + fbH = scale_y; + } + + eff_height = fbH; + if (appData.yCrop > 0) { + eff_height = appData.yCrop; + if (scale_y > 0) { + eff_height = scale_round(eff_height, scale_factor_y); + } + } + + toplevelWidth = fbW; + toplevelHeight = eff_height; + + fs_ungrab(0); + + if (do_net_wm) { + net_wm_fullscreen(0); + } else { + XtUnmapWidget(toplevel); + } - if (appData.grabKeyboard) - XtUngrabKeyboard(desktop, CurrentTime); - - XtUnmapWidget(toplevel); - - XtResizeWidget(toplevel, + XtResizeWidget(toplevel, viewportWidth - scrollbarWidth, viewportHeight - scrollbarHeight, 0); - XtResizeWidget(viewport, + XtResizeWidget(viewport, viewportWidth - scrollbarWidth, viewportHeight - scrollbarHeight, 0); - XtVaSetValues(viewport, XtNforceBars, False, NULL); + XtVaSetValues(viewport, XtNforceBars, False, NULL); - XtUnmanageChild(viewport); + XtUnmanageChild(viewport); - XtVaSetValues(viewport, + XtVaSetValues(viewport, XtNhorizDistance, 0, XtNvertDistance, 0, XtNleft, XtChainLeft, @@ -235,24 +607,42 @@ XtNbottom, XtChainBottom, NULL); - XtManageChild(viewport); - - XtVaSetValues(toplevel, XtNoverrideRedirect, False, NULL); - - if ((toplevelWidth + appData.wmDecorationWidth) >= dpyWidth) - toplevelWidth = dpyWidth - appData.wmDecorationWidth; - - if ((toplevelHeight + appData.wmDecorationHeight) >= dpyHeight) - toplevelHeight = dpyHeight - appData.wmDecorationHeight; - - XtResizeWidget(toplevel, toplevelWidth, toplevelHeight, 0); - - XtMapWidget(toplevel); - XSync(dpy, False); + XtManageChild(viewport); - /* Set the popup back to non-overrideRedirect */ - - XtVaSetValues(popup, XtNoverrideRedirect, False, NULL); + if (!do_net_wm) { + XtVaSetValues(toplevel, XtNoverrideRedirect, False, NULL); +#if 0 + XtVaSetValues(viewport, XtNoverrideRedirect, False, NULL); + XtVaSetValues(desktop, XtNoverrideRedirect, False, NULL); +#endif + XtVaSetValues(popup, XtNoverrideRedirect, False, NULL); + } + + if ((toplevelWidth + appData.wmDecorationWidth) >= dpyWidth) + toplevelWidth = dpyWidth - appData.wmDecorationWidth; + + if ((toplevelHeight + appData.wmDecorationHeight) >= dpyHeight) + toplevelHeight = dpyHeight - appData.wmDecorationHeight; + + XtResizeWidget(toplevel, toplevelWidth, toplevelHeight, 0); + + if (!do_net_wm) { + XtMapWidget(toplevel); + } + XSync(dpy, False); + + /* Set the popup back to non-overrideRedirect */ + + XtVaSetValues(popup, XtNoverrideRedirect, False, NULL); + + if (!do_net_wm) { + int x = (dpyWidth - toplevelWidth) / 2; + int y = (dpyHeight - toplevelHeight) / 2; + if (x > 0 && y > 0) { + XSync(dpy, False); + XMoveWindow(dpy, XtWindow(toplevel), x, y); + } + } } @@ -264,10 +654,12 @@ void SetFullScreenState(Widget w, XEvent *ev, String *params, Cardinal *num_params) { - if (appData.fullScreen) - XtVaSetValues(w, XtNstate, True, NULL); - else - XtVaSetValues(w, XtNstate, False, NULL); + if (appData.fullScreen) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} } @@ -278,11 +670,12 @@ void ToggleFullScreen(Widget w, XEvent *ev, String *params, Cardinal *num_params) { - if (appData.fullScreen) { - FullScreenOff(); - } else { - FullScreenOn(); - } + if (appData.fullScreen) { + FullScreenOff(); + } else { + FullScreenOn(); + } + if (w || ev || params || num_params) {} } @@ -294,84 +687,226 @@ Bool BumpScroll(XEvent *ev) { - scrollLeft = scrollRight = scrollUp = scrollDown = False; + scrollLeft = scrollRight = scrollUp = scrollDown = False; - if (ev->xmotion.x_root >= dpyWidth - 3) - scrollRight = True; - else if (ev->xmotion.x_root <= 2) - scrollLeft = True; - - if (ev->xmotion.y_root >= dpyHeight - 3) - scrollDown = True; - else if (ev->xmotion.y_root <= 2) - scrollUp = True; - - if (scrollLeft || scrollRight || scrollUp || scrollDown) { - if (timerSet) - return True; - - XtVaGetValues(desktop, XtNx, &desktopX, XtNy, &desktopY, NULL); - desktopX = -desktopX; - desktopY = -desktopY; - - return DoBumpScroll(); - } - - if (timerSet) { - XtRemoveTimeOut(timer); - timerSet = False; - } + if (ev->xmotion.x_root >= dpyWidth - 3) + scrollRight = True; + else if (ev->xmotion.x_root <= 2) + scrollLeft = True; + + if (ev->xmotion.y_root >= dpyHeight - 3) + scrollDown = True; + else if (ev->xmotion.y_root <= 2) + scrollUp = True; + + if (scrollLeft || scrollRight || scrollUp || scrollDown) { + if (timerSet) + return True; + + XtVaGetValues(desktop, XtNx, &desktopX, XtNy, &desktopY, NULL); + desktopX = -desktopX; + desktopY = -desktopY; + + return DoBumpScroll(); + } + + if (timerSet) { + XtRemoveTimeOut(timer); + timerSet = False; + } - return False; + return False; } static Bool DoBumpScroll() { - int oldx = desktopX, oldy = desktopY; - - if (scrollRight) { - if (desktopX < si.framebufferWidth - dpyWidth) { - desktopX += appData.bumpScrollPixels; - if (desktopX > si.framebufferWidth - dpyWidth) - desktopX = si.framebufferWidth - dpyWidth; - } - } else if (scrollLeft) { - if (desktopX > 0) { - desktopX -= appData.bumpScrollPixels; - if (desktopX < 0) - desktopX = 0; - } - } - - if (scrollDown) { - if (desktopY < si.framebufferHeight - dpyHeight) { - desktopY += appData.bumpScrollPixels; - if (desktopY > si.framebufferHeight - dpyHeight) - desktopY = si.framebufferHeight - dpyHeight; - } - } else if (scrollUp) { - if (desktopY > 0) { - desktopY -= appData.bumpScrollPixels; - if (desktopY < 0) - desktopY = 0; - } - } - - if (oldx != desktopX || oldy != desktopY) { - XawViewportSetCoordinates(viewport, desktopX, desktopY); - timer = XtAppAddTimeOut(appContext, appData.bumpScrollTime, - BumpScrollTimerCallback, NULL); - timerSet = True; - return True; - } + int oldx = desktopX, oldy = desktopY; + int fbW = si.framebufferWidth; + int fbH = si.framebufferHeight; + + if (scale_x > 0) { + fbW = scale_x; + fbH = scale_y; + } + + if (scrollRight) { + if (desktopX < fbW - dpyWidth) { + desktopX += appData.bumpScrollPixels; + if (desktopX > fbW - dpyWidth) { + desktopX = fbW - dpyWidth; + } + } + } else if (scrollLeft) { + if (desktopX > 0) { + desktopX -= appData.bumpScrollPixels; + if (desktopX < 0) { + desktopX = 0; + } + } + } + + if (scrollDown) { + int ycrop = appData.yCrop; + if (scale_y > 0) { + ycrop = scale_round(ycrop, scale_factor_y); + } + if (ycrop > 0 && desktopY + dpyHeight >= ycrop) { + ; + } else if (desktopY < fbH - dpyHeight) { + desktopY += appData.bumpScrollPixels; + if (desktopY > fbH - dpyHeight) { + desktopY = fbH - dpyHeight; + } + } + } else if (scrollUp) { + if (desktopY > 0) { + desktopY -= appData.bumpScrollPixels; + if (desktopY < 0) { + desktopY = 0; + } + } + } + + if (oldx != desktopX || oldy != desktopY) { + XawViewportSetCoordinates(viewport, desktopX, desktopY); + timer = XtAppAddTimeOut(appContext, appData.bumpScrollTime, BumpScrollTimerCallback, NULL); + timerSet = True; + return True; + } - timerSet = False; - return False; + timerSet = False; + return False; } static void BumpScrollTimerCallback(XtPointer clientData, XtIntervalId *id) { - DoBumpScroll(); + DoBumpScroll(); + if (clientData || id) {} +} + +/* not working: */ + +Bool +JumpScroll(int up, int vert) { + scrollLeft = scrollRight = scrollUp = scrollDown = False; + + + if (appData.fullScreen) { + return True; + } + fprintf(stderr, "JumpScroll(%d, %d)\n", up, vert); + + if (vert) { + if (up) { + scrollUp = True; + } else { + scrollDown = True; + } + } else { + if (up) { + scrollRight = True; + } else { + scrollLeft = True; + } + } + + if (scrollLeft || scrollRight || scrollUp || scrollDown) { + if (timerSet) { + return True; + } + + XtVaGetValues(desktop, XtNx, &desktopX, XtNy, &desktopY, NULL); + desktopX = -desktopX; + desktopY = -desktopY; + return DoJumpScroll(); + } + + if (timerSet) { + XtRemoveTimeOut(timer); + timerSet = False; + } + + return False; +} + +static Bool +DoJumpScroll() { + int oldx = desktopX, oldy = desktopY; + int jumpH, jumpV; + int fbW = si.framebufferWidth; + int fbH = si.framebufferHeight; + + if (scale_x > 0) { + fbW = scale_x; + fbH = scale_y; + } + jumpH = fbW / 4; + jumpV = fbH / 4; + + if (scrollRight) { + if (desktopX < fbW - dpyWidth) { + desktopX += jumpH; + if (desktopX > fbW - dpyWidth) + desktopX = fbW - dpyWidth; + } + } else if (scrollLeft) { + if (desktopX > 0) { + desktopX -= jumpH; + if (desktopX < 0) + desktopX = 0; + } + } + + if (scrollDown) { + if (appData.yCrop > 0 && desktopY + dpyHeight >= appData.yCrop) { + ; + } else if (desktopY < fbH - dpyHeight) { + desktopY += jumpV; + if (desktopY > fbH - dpyHeight) + desktopY = fbH - dpyHeight; + } + } else if (scrollUp) { + if (desktopY > 0) { + desktopY -= jumpV; + if (desktopY < 0) + desktopY = 0; + } + } + + if (oldx != desktopX || oldy != desktopY) { + XawViewportSetCoordinates(viewport, desktopX, desktopY); + timer = XtAppAddTimeOut(appContext, appData.bumpScrollTime, + JumpScrollTimerCallback, NULL); + timerSet = True; + return True; + } + + timerSet = False; + return False; +} + +static void +JumpScrollTimerCallback(XtPointer clientData, XtIntervalId *id) { + DoJumpScroll(); + if (clientData || id) {} +} +void JumpRight(Widget w, XEvent *ev, String *params, Cardinal *num_params) { + JumpScroll(1, 0); + if (w || ev || params || num_params) {} +} +void JumpLeft(Widget w, XEvent *ev, String *params, Cardinal *num_params) { + JumpScroll(0, 0); + if (w || ev || params || num_params) {} +} +void JumpUp(Widget w, XEvent *ev, String *params, Cardinal *num_params) { + JumpScroll(1, 1); + if (w || ev || params || num_params) {} } +void JumpDown(Widget w, XEvent *ev, String *params, Cardinal *num_params) { + JumpScroll(0, 1); + if (w || ev || params || num_params) {} +} + + diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/h2html.pl vnc_unixsrc/vncviewer/h2html.pl --- vnc_unixsrc.orig/vncviewer/h2html.pl 1969-12-31 19:00:00.000000000 -0500 +++ vnc_unixsrc/vncviewer/h2html.pl 2008-08-30 20:34:45.000000000 -0400 @@ -0,0 +1,10 @@ +#!/usr/bin/perl + +open(HELP, "./vncviewer -help|"); + +while (<HELP>) { + $_ =~ s/&/&/g; + $_ =~ s/</</g; + $_ =~ s/>/>/g; + print; +} diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/hextile.c vnc_unixsrc/vncviewer/hextile.c --- vnc_unixsrc.orig/vncviewer/hextile.c 2007-02-17 22:33:46.000000000 -0500 +++ vnc_unixsrc/vncviewer/hextile.c 2009-10-16 22:54:40.000000000 -0400 @@ -30,6 +30,21 @@ #define CARDBPP CONCAT2E(CARD,BPP) #define GET_PIXEL CONCAT2E(GET_PIXEL,BPP) +#define FillRectangle(x, y, w, h, color) \ + { \ + XGCValues _gcv; \ + _gcv.foreground = color; \ + if (!appData.useXserverBackingStore) { \ + FillScreen(x, y, w, h, _gcv.foreground); \ + } else { \ + XChangeGC(dpy, gc, GCForeground, &_gcv); \ + XFillRectangle(dpy, desktopWin, gc, x, y, w, h); \ + } \ + } + +extern int skip_maybe_sync; +extern void maybe_sync(int w, int h); + static Bool HandleHextileBPP (int rx, int ry, int rw, int rh) { @@ -41,21 +56,43 @@ int sx, sy, sw, sh; CARD8 subencoding; CARD8 nSubrects; + int irect = 0, nrects = (rw * rh) / (16 * 16); + static int nosync_ycrop = -1; + + if (nosync_ycrop < 0) { + nosync_ycrop = 0; + if (getenv("HEXTILE_YCROP_TOO")) { + nosync_ycrop = 1; + } + } for (y = ry; y < ry+rh; y += 16) { for (x = rx; x < rx+rw; x += 16) { w = h = 16; - if (rx+rw - x < 16) + if (rx+rw - x < 16) { w = rx+rw - x; - if (ry+rh - y < 16) + } + if (ry+rh - y < 16) { h = ry+rh - y; + } + + if (nrects > 400 && (appData.yCrop == 0 || nosync_ycrop)) { + skip_maybe_sync = 0; + if (irect++ % 2000 != 0) { + if (x < rx+rw-16 || y < ry+rh-16) { + skip_maybe_sync = 1; + } + } + } - if (!ReadFromRFBServer((char *)&subencoding, 1)) + if (!ReadFromRFBServer((char *)&subencoding, 1)) { return False; + } if (subencoding & rfbHextileRaw) { - if (!ReadFromRFBServer(buffer, w * h * (BPP / 8))) + if (!ReadFromRFBServer(buffer, w * h * (BPP / 8))) { return False; + } CopyDataToScreen(buffer, x, y, w, h); continue; @@ -66,14 +103,25 @@ return False; #if (BPP == 8) - if (appData.useBGR233) + if (appData.useBGR233) { gcv.foreground = BGR233ToPixel[bg]; - else + } else +#endif +#if (BPP == 16) + if (appData.useBGR565) { + gcv.foreground = BGR565ToPixel[bg]; + } else #endif + { gcv.foreground = bg; + } - XChangeGC(dpy, gc, GCForeground, &gcv); - XFillRectangle(dpy, desktopWin, gc, x, y, w, h); +#if 0 + XChangeGC(dpy, gc, GCForeground, &gcv); + XFillRectangle(dpy, desktopWin, gc, x, y, w, h); +#else + FillRectangle(x, y, w, h, gcv.foreground); +#endif if (subencoding & rfbHextileForegroundSpecified) if (!ReadFromRFBServer((char *)&fg, sizeof(fg))) @@ -101,14 +149,25 @@ sh = rfbHextileExtractH(*ptr); ptr++; #if (BPP == 8) - if (appData.useBGR233) + if (appData.useBGR233) { gcv.foreground = BGR233ToPixel[fg]; - else + } else #endif +#if (BPP == 16) + if (appData.useBGR565) { + gcv.foreground = BGR565ToPixel[fg]; + } else +#endif + { gcv.foreground = fg; + } - XChangeGC(dpy, gc, GCForeground, &gcv); - XFillRectangle(dpy, desktopWin, gc, x+sx, y+sy, sw, sh); +#if 0 + XChangeGC(dpy, gc, GCForeground, &gcv); + XFillRectangle(dpy, desktopWin, gc, x+sx, y+sy, sw, sh); +#else + FillRectangle(x+sx, y+sy, sw, sh, gcv.foreground); +#endif } } else { @@ -116,13 +175,22 @@ return False; #if (BPP == 8) - if (appData.useBGR233) + if (appData.useBGR233) { gcv.foreground = BGR233ToPixel[fg]; - else + } else #endif +#if (BPP == 16) + if (appData.useBGR565) { + gcv.foreground = BGR565ToPixel[fg]; + } else +#endif + { gcv.foreground = fg; + } +#if 0 XChangeGC(dpy, gc, GCForeground, &gcv); +#endif for (i = 0; i < nSubrects; i++) { sx = rfbHextileExtractX(*ptr); @@ -131,7 +199,11 @@ sw = rfbHextileExtractW(*ptr); sh = rfbHextileExtractH(*ptr); ptr++; +#if 0 XFillRectangle(dpy, desktopWin, gc, x+sx, y+sy, sw, sh); +#else + FillRectangle(x+sx, y+sy, sw, sh, gcv.foreground); +#endif } } } @@ -139,3 +211,5 @@ return True; } + +#undef FillRectangle diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/listen.c vnc_unixsrc/vncviewer/listen.c --- vnc_unixsrc.orig/vncviewer/listen.c 2001-01-16 03:07:57.000000000 -0500 +++ vnc_unixsrc/vncviewer/listen.c 2010-04-11 23:14:21.000000000 -0400 @@ -32,14 +32,88 @@ #define FLASHDELAY 1 /* seconds */ Bool listenSpecified = False; +pid_t listenParent = 0; int listenPort = 0, flashPort = 0; +#if 0 static Font flashFont; - static void getFlashFont(Display *d); static void flashDisplay(Display *d, char *user); +#endif + static Bool AllXEventsPredicate(Display *d, XEvent *ev, char *arg); +void raiseme(int force); + +static int accept_popup_check(int *argc, char **argv, char *sip, char *sih) { + char line[16]; + char msg[1000]; + int dopopup = 1; + + if (!getenv("SSVNC_ACCEPT_POPUP")) { + return 1; + } + + if (!dopopup && use_tty()) { + raiseme(1); + fprintf(stderr, "Accept VNC connection? y/[n] "); + fgets(line, sizeof(line), stdin); + if (!strchr(line, 'y') && !strchr(line, 'Y')) { + fprintf(stderr, "Refusing connection.\n"); + return 0; + } else { + fprintf(stderr, "Accepting connection.\n"); + return 1; + } + } else { + int pid, pid2, accept_it = 0; + + pid = fork(); + if (pid == -1) { + perror("fork"); + exit(1); + } + if (pid == 0) { + char *geometry = "2x2+0+0"; + String fb[] = { "*message.Scroll: whenNeeded", NULL}; + close(rfbsock); + + toplevel = XtAppInitialize(&appContext, "Ssvnc", cmdLineOptions, numCmdLineOptions, + argc, argv, fb, NULL, 0); + XtVaSetValues(toplevel, XtNmaxWidth, 2, XtNmaxHeight, 2, NULL); + XtVaSetValues(toplevel, XtNgeometry, geometry, NULL); + XtRealizeWidget(toplevel); + dpy = XtDisplay(toplevel); + sprintf(msg, "\n(LISTEN) Reverse VNC connection from IP: %s\n Hostname: %s\n\n", sip, sih); + strcat(msg, "Accept or Reject VNC connection?"); + if (CreateMsg(msg, 2)) { + XCloseDisplay(dpy); + exit(0); + } else { + XCloseDisplay(dpy); + exit(1); + } + } else { + int status; + pid2 = waitpid(pid, &status, 0); + fprintf(stderr, "waitpid: %d/%d status: %d\n", pid, pid2, status); + if (pid2 == pid) { + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + accept_it = 1; + } + } + } + if (accept_it) { + fprintf(stderr, "Accepting connection.\n"); + return 1; + } else { + fprintf(stderr, "Refusing connection.\n"); + return 0; + } + } + return 0; +} + /* * listenForIncomingConnections() - listen for incoming connections from * servers, and fork a new process to deal with each connection. We must do @@ -47,145 +121,291 @@ * cope with forking very well. */ +extern char *accept6_hostname; +extern char *accept6_ipaddr; + void listenForIncomingConnections(int *argc, char **argv, int listenArgIndex) { - Display *d; - XEvent ev; - int listenSocket, flashSocket, sock; - fd_set fds; - char flashUser[256]; - int n; - int i; - char *displayname = NULL; - - listenSpecified = True; - - for (i = 1; i < *argc; i++) { - if (strcmp(argv[i], "-display") == 0 && i+1 < *argc) { - displayname = argv[i+1]; - } - } + Display *d; + XEvent ev; + int listenSocket, listenSocket6, flashSocket, sock; + fd_set fds; + char flashUser[256]; + int n; + int i; + char *displayname = NULL; + int children = 0; + int totalconn = 0, maxconn = 0; + + listenSpecified = True; + listenParent = getpid(); + + for (i = 1; i < *argc; i++) { + if (strcmp(argv[i], "-display") == 0 && i+1 < *argc) { + displayname = argv[i+1]; + } + } + if (sock || flashUser || n) {} - if (listenArgIndex+1 < *argc && argv[listenArgIndex+1][0] >= '0' && + if (listenArgIndex+1 < *argc && argv[listenArgIndex+1][0] >= '0' && argv[listenArgIndex+1][0] <= '9') { - listenPort = LISTEN_PORT_OFFSET + atoi(argv[listenArgIndex+1]); - flashPort = FLASH_PORT_OFFSET + atoi(argv[listenArgIndex+1]); - removeArgs(argc, argv, listenArgIndex, 2); + listenPort = LISTEN_PORT_OFFSET + atoi(argv[listenArgIndex+1]); + flashPort = FLASH_PORT_OFFSET + atoi(argv[listenArgIndex+1]); + removeArgs(argc, argv, listenArgIndex, 2); - } else { + } else { - char *display; - char *colonPos; - struct utsname hostinfo; + char *display; + char *colonPos; + struct utsname hostinfo; - removeArgs(argc, argv, listenArgIndex, 1); + removeArgs(argc, argv, listenArgIndex, 1); - display = XDisplayName(displayname); - colonPos = strchr(display, ':'); + display = XDisplayName(displayname); + colonPos = strchr(display, ':'); - uname(&hostinfo); + uname(&hostinfo); - if (colonPos && ((colonPos == display) || - (strncmp(hostinfo.nodename, display, - strlen(hostinfo.nodename)) == 0))) { + if (colonPos && ((colonPos == display) || + (strncmp(hostinfo.nodename, display, + strlen(hostinfo.nodename)) == 0))) { - listenPort = LISTEN_PORT_OFFSET + atoi(colonPos+1); - flashPort = FLASH_PORT_OFFSET + atoi(colonPos+1); + listenPort = LISTEN_PORT_OFFSET + atoi(colonPos+1); + flashPort = FLASH_PORT_OFFSET + atoi(colonPos+1); - } else { - fprintf(stderr,"%s: cannot work out which display number to " - "listen on.\n", programName); - fprintf(stderr,"Please specify explicitly with -listen <num>\n"); - exit(1); - } - } + } else { + fprintf(stderr,"%s: cannot work out which display number to " + "listen on.\n", programName); + fprintf(stderr,"Please specify explicitly with -listen <num>\n"); + exit(1); + } - if (!(d = XOpenDisplay(displayname))) { - fprintf(stderr,"%s: unable to open display %s\n", - programName, XDisplayName(displayname)); - exit(1); - } + } - getFlashFont(d); + if (!(d = XOpenDisplay(displayname))) { + fprintf(stderr,"%s: unable to open display %s\n", + programName, XDisplayName(displayname)); + exit(1); + } - listenSocket = ListenAtTcpPort(listenPort); - flashSocket = ListenAtTcpPort(flashPort); +#if 0 + getFlashFont(d); +#endif - if ((listenSocket < 0) || (flashSocket < 0)) exit(1); + listenSocket = ListenAtTcpPort(listenPort); + listenSocket6 = ListenAtTcpPort6(listenPort); + +#if 0 + flashSocket = ListenAtTcpPort(flashPort); +#endif + flashSocket = 1234; + + if (listenSocket < 0 && listenSocket6 < 0) { + fprintf(stderr,"%s -listen: could not obtain a listening socket on port %d\n", + programName, listenPort); + exit(1); + } - fprintf(stderr,"%s -listen: Listening on port %d (flash port %d)\n", - programName,listenPort,flashPort); - fprintf(stderr,"%s -listen: Command line errors are not reported until " + fprintf(stderr,"%s -listen: Listening on port %d ipv4_fd: %d ipv6_fd: %d\n", + programName, listenPort, listenSocket, listenSocket6); + fprintf(stderr,"%s -listen: Cmdline errors are not reported until " "a connection comes in.\n", programName); - while (True) { + /* this will only work if X events drives this loop -- they don't */ + if (getenv("SSVNC_MAX_LISTEN")) { + maxconn = atoi(getenv("SSVNC_MAX_LISTEN")); + } - /* reap any zombies */ - int status, pid; - while ((pid= wait3(&status, WNOHANG, (struct rusage *)0))>0); - - /* discard any X events */ - while (XCheckIfEvent(d, &ev, AllXEventsPredicate, NULL)) - ; - - FD_ZERO(&fds); - - FD_SET(flashSocket, &fds); - FD_SET(listenSocket, &fds); - FD_SET(ConnectionNumber(d), &fds); - - select(FD_SETSIZE, &fds, NULL, NULL, NULL); - - if (FD_ISSET(flashSocket, &fds)) { - - sock = AcceptTcpConnection(flashSocket); - if (sock < 0) exit(1); - n = read(sock, flashUser, 255); - if (n > 0) { - flashUser[n] = 0; - flashDisplay(d, flashUser); - } else { - flashDisplay(d, NULL); - } - close(sock); - } + while (True) { + int lsock = -1; - if (FD_ISSET(listenSocket, &fds)) { - rfbsock = AcceptTcpConnection(listenSocket); - if (rfbsock < 0) exit(1); - if (!SetNonBlocking(rfbsock)) exit(1); + /* reap any zombies */ + int status, pid; + while ((pid = wait3(&status, WNOHANG, (struct rusage *)0))>0) { + if (pid > 0 && children > 0) { + children--; + /* this will only work if X events drives this loop -- they don't */ + if (maxconn > 0 && totalconn >= maxconn) { + fprintf(stderr,"%s -listen: Finished final connection %d\n", + programName, maxconn); + exit(0); + } + } + } - XCloseDisplay(d); + /* discard any X events */ + while (XCheckIfEvent(d, &ev, AllXEventsPredicate, NULL)) { + ; + } - /* Now fork off a new process to deal with it... */ + FD_ZERO(&fds); - switch (fork()) { +#if 0 + FD_SET(flashSocket, &fds); +#endif + if (listenSocket >= 0) { + FD_SET(listenSocket, &fds); + } + if (listenSocket6 >= 0) { + FD_SET(listenSocket6, &fds); + } + FD_SET(ConnectionNumber(d), &fds); - case -1: - perror("fork"); - exit(1); + select(FD_SETSIZE, &fds, NULL, NULL, NULL); - case 0: - /* child - return to caller */ - close(listenSocket); - close(flashSocket); - return; + while ((pid = wait3(&status, WNOHANG, (struct rusage *)0))>0) { + if (pid > 0 && children > 0) { + children--; + if (maxconn > 0 && totalconn >= maxconn) { + fprintf(stderr,"%s -listen: Finished final connection %d\n", + programName, maxconn); + exit(0); + } + } + } - default: - /* parent - go round and listen again */ - close(rfbsock); - if (!(d = XOpenDisplay(displayname))) { - fprintf(stderr,"%s: unable to open display %s\n", - programName, XDisplayName(displayname)); - exit(1); +#if 0 + if (FD_ISSET(flashSocket, &fds)) { + sock = AcceptTcpConnection(flashSocket); + if (sock < 0) exit(1); + n = read(sock, flashUser, 255); + if (n > 0) { + flashUser[n] = 0; + flashDisplay(d, flashUser); + } else { + flashDisplay(d, NULL); + } + close(sock); + } +#endif + + lsock = -1; + if (listenSocket >= 0 && FD_ISSET(listenSocket, &fds)) { + lsock = listenSocket; + } else if (listenSocket6 >= 0 && FD_ISSET(listenSocket6, &fds)) { + lsock = listenSocket6; + } + + if (lsock >= 0) { + int multi_ok = 0; + char *sml = getenv("SSVNC_MULTIPLE_LISTEN"); + char *sip = NULL; + char *sih = NULL; + + if (lsock == listenSocket) { + rfbsock = AcceptTcpConnection(lsock); + } else { + rfbsock = AcceptTcpConnection6(lsock); + } + + if (sml != NULL) { + if (strstr(sml, "MAX:") == sml || strstr(sml, "max:") == sml) { + char *q = strchr(sml, ':'); + int maxc = atoi(q+1); + if (maxc == 0 && strcmp(q+1, "0")) { + maxc = -99; + } + if (maxc < 0) { + fprintf(stderr, "invalid SSVNC_MULTIPLE_LISTEN=MAX:n, %s, must be 0 or positive, using 1\n", sml); + } else if (maxc == 0) { + multi_ok = 1; + } else if (children < maxc) { + multi_ok = 1; + } + } else if (strcmp(sml, "") && strcmp(sml, "0")) { + multi_ok = 1; + } + } + + if (rfbsock < 0) exit(1); + if (!SetNonBlocking(rfbsock)) exit(1); + + if (children > 0 && !multi_ok) { + fprintf(stderr,"\n"); + fprintf(stderr,"%s: denying extra incoming connection (%d already)\n", + programName, children); + fprintf(stderr,"%s: to override: use '-multilisten' or set SSVNC_MULTIPLE_LISTEN=1\n", + programName); + fprintf(stderr,"\n"); + close(rfbsock); + rfbsock = -1; + continue; + } + + if (lsock == listenSocket) { + sip = get_peer_ip(rfbsock); + if (strlen(sip) > 100) sip = "0.0.0.0"; + sih = ip2host(sip); + if (strlen(sih) > 300) sih = "unknown"; + } else { + if (accept6_hostname != NULL) { + sip = accept6_ipaddr; + accept6_ipaddr = NULL; + sih = accept6_hostname; + accept6_hostname = NULL; + } else { + sip = "unknown"; + sih = "unknown"; + } + } + + fprintf(stderr, "\n"); + fprintf(stderr, "(LISTEN) Reverse VNC connection from IP: %s\n", sip); + fprintf(stderr, " Hostname: %s\n\n", sih); + + if (sml == NULL && !accept_popup_check(argc, argv, sip, sih)) { + close(rfbsock); + rfbsock = -1; + continue; + } + + totalconn++; + + XCloseDisplay(d); + + /* Now fork off a new process to deal with it... */ + + switch (fork()) { + + case -1: + perror("fork"); + exit(1); + + case 0: + /* child - return to caller */ + close(listenSocket); +#if 0 + close(flashSocket); +#endif + if (sml != NULL && !accept_popup_check(argc, argv, sip, sih)) { + close(rfbsock); + rfbsock = -1; + exit(0); + } + return; + + default: + /* parent - go round and listen again */ + children++; + close(rfbsock); + if (!(d = XOpenDisplay(displayname))) { + fprintf(stderr,"%s: unable to open display %s\n", + programName, XDisplayName(displayname)); + exit(1); + } +#if 0 + getFlashFont(d); +#endif + fprintf(stderr,"\n\n%s -listen: Listening on port %d\n", + programName,listenPort); + fprintf(stderr,"%s -listen: Cmdline errors are not reported until " + "a connection comes in.\n\n", programName); + break; + } } - getFlashFont(d); - break; - } } - } } @@ -193,9 +413,16 @@ * getFlashFont */ +#if 0 static void getFlashFont(Display *d) { + +#if 1 + /* no longer used */ + if (d) {} + return; +#else char fontName[256]; char **fontNames; int nFontNames; @@ -209,6 +436,9 @@ sprintf(fontName,"fixed"); } flashFont = XLoadFont(d, fontName); + +#endif + } @@ -219,6 +449,11 @@ static void flashDisplay(Display *d, char *user) { +#if 1 + /* no longer used */ + if (d || user) {} + return; +#else Window w1, w2, w3, w4; XSetWindowAttributes attr; @@ -284,7 +519,11 @@ XDestroyWindow(d, w3); XDestroyWindow(d, w4); XFlush(d); + +#endif + } +#endif /* * AllXEventsPredicate is needed to make XCheckIfEvent return all events. @@ -293,5 +532,6 @@ static Bool AllXEventsPredicate(Display *d, XEvent *ev, char *arg) { - return True; + if (d || ev || arg) {} + return True; } diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/misc.c vnc_unixsrc/vncviewer/misc.c --- vnc_unixsrc.orig/vncviewer/misc.c 2003-01-15 02:58:32.000000000 -0500 +++ vnc_unixsrc/vncviewer/misc.c 2010-02-25 22:44:09.000000000 -0500 @@ -23,6 +23,7 @@ #include <vncviewer.h> #include <signal.h> +#include <sys/wait.h> #include <fcntl.h> static void CleanupSignalHandler(int sig); @@ -33,12 +34,20 @@ Dimension dpyWidth, dpyHeight; Atom wmDeleteWindow, wmState; +int fullscreen_startup = 0; static Bool xloginIconified = False; static XErrorHandler defaultXErrorHandler; static XIOErrorHandler defaultXIOErrorHandler; static XtErrorHandler defaultXtErrorHandler; +int XError_ign = 0; + +void check_tall(void); +int guessCrop(void); +void get_scale_values(double *fx, double *fy); +int scale_round(int n, double factor); +Bool SendTextChatFinished(void); /* * ToplevelInitBeforeRealization sets the title, geometry and other resources @@ -48,87 +57,122 @@ void ToplevelInitBeforeRealization() { - char *titleFormat; - char *title; - char *geometry; - - XtVaGetValues(toplevel, XtNtitle, &titleFormat, NULL); - title = XtMalloc(strlen(titleFormat) + strlen(desktopName) + 1); - sprintf(title, titleFormat, desktopName); - XtVaSetValues(toplevel, XtNtitle, title, XtNiconName, title, NULL); - - XtVaSetValues(toplevel, XtNmaxWidth, si.framebufferWidth, - XtNmaxHeight, si.framebufferHeight, NULL); - - dpyWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy)); - dpyHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy)); - - if (appData.fullScreen) { - - /* full screen - set position to 0,0, but defer size calculation until - widgets are realized */ - - XtVaSetValues(toplevel, XtNoverrideRedirect, True, - XtNgeometry, "+0+0", NULL); - - } else { - - /* not full screen - work out geometry for middle of screen unless - specified by user */ - - XtVaGetValues(toplevel, XtNgeometry, &geometry, NULL); - - if (geometry == NULL) { - Dimension toplevelX, toplevelY; - Dimension toplevelWidth = si.framebufferWidth; - Dimension toplevelHeight = si.framebufferHeight; - - if ((toplevelWidth + appData.wmDecorationWidth) >= dpyWidth) - toplevelWidth = dpyWidth - appData.wmDecorationWidth; - - if ((toplevelHeight + appData.wmDecorationHeight) >= dpyHeight) - toplevelHeight = dpyHeight - appData.wmDecorationHeight; - - toplevelX = (dpyWidth - toplevelWidth - appData.wmDecorationWidth) / 2; - - toplevelY = (dpyHeight - toplevelHeight - appData.wmDecorationHeight) /2; - - /* set position via "geometry" so that window manager thinks it's a - user-specified position and therefore honours it */ - - geometry = XtMalloc(256); - - sprintf(geometry, "%dx%d+%d+%d", - toplevelWidth, toplevelHeight, toplevelX, toplevelY); - XtVaSetValues(toplevel, XtNgeometry, geometry, NULL); - } - } + char *titleFormat; + char *title; + char *geometry; + int h = si.framebufferHeight; + int w = si.framebufferWidth; + + check_tall(); + if (appData.yCrop < 0) { + appData.yCrop = guessCrop(); + fprintf(stderr, "Set -ycrop to: %d\n", appData.yCrop); + if (appData.yCrop > 0) { + h = appData.yCrop; + } + } + + XtVaGetValues(toplevel, XtNtitle, &titleFormat, NULL); + title = XtMalloc(strlen(titleFormat) + strlen(desktopName) + 1); + sprintf(title, titleFormat, desktopName); + XtVaSetValues(toplevel, XtNtitle, title, XtNiconName, title, NULL); + + if (appData.scale != NULL) { + /* switched to not scaled */ + double frac_x, frac_y; + get_scale_values(&frac_x, &frac_y); + if (frac_x > 0.0 && frac_y > 0.0) { + w = scale_round(w, frac_x); + h = scale_round(h, frac_y); + } + } + XtVaSetValues(toplevel, XtNmaxWidth, w, XtNmaxHeight, h, NULL); + + dpyWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy)); + dpyHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy)); + + if (appData.fullScreen) { + /* full screen - set position to 0,0, but defer size calculation until widgets are realized */ + + if (!net_wm_supported()) { + XtVaSetValues(toplevel, XtNoverrideRedirect, True, XtNgeometry, "+0+0", NULL); + } else { + fullscreen_startup = 1; + } + + } else { + + /* not full screen - work out geometry for middle of screen unless specified by user */ + + XtVaGetValues(toplevel, XtNgeometry, &geometry, NULL); + + if (geometry == NULL) { + Dimension toplevelX, toplevelY; + Dimension toplevelWidth = w; + Dimension toplevelHeight = h; + + if ((toplevelWidth + appData.wmDecorationWidth) >= dpyWidth) { + toplevelWidth = dpyWidth - appData.wmDecorationWidth; + } + + if ((toplevelHeight + appData.wmDecorationHeight) >= dpyHeight) { + toplevelHeight = dpyHeight - appData.wmDecorationHeight; + } + + toplevelX = (dpyWidth - toplevelWidth - appData.wmDecorationWidth) / 2; + toplevelY = (dpyHeight - toplevelHeight - appData.wmDecorationHeight) /2; + + if (appData.appShare) { + int X = appshare_x_hint; + int Y = appshare_y_hint; + if (appData.scale) { + double fx = 1.0, fy = 1.0; + get_scale_values(&fx, &fy); + if (fx > 0.0 && fy > 0.0) { + X *= fx; + Y *= fy; + } + } + if (appshare_x_hint != appshare_0_hint) { + toplevelX = X; + } + if (appshare_y_hint != appshare_0_hint) { + toplevelY = Y; + } + } + + /* set position via "geometry" so that window manager thinks it's a + user-specified position and therefore honours it */ + + geometry = XtMalloc(256); + + sprintf(geometry, "%dx%d+%d+%d", toplevelWidth, toplevelHeight, toplevelX, toplevelY); + fprintf(stderr, "geometry: %s ycrop: %d\n", geometry, appData.yCrop); + XtVaSetValues(toplevel, XtNgeometry, geometry, NULL); + } + } /* Test if the keyboard is grabbed. If so, it's probably because the XDM login window is up, so try iconifying it to release the grab */ - if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), False, GrabModeSync, - GrabModeSync, CurrentTime) == GrabSuccess) { - XUngrabKeyboard(dpy, CurrentTime); - } else { - wmState = XInternAtom(dpy, "WM_STATE", False); - - if (IconifyNamedWindow(DefaultRootWindow(dpy), "xlogin", False)) { - xloginIconified = True; - XSync(dpy, False); - sleep(1); - } - } - - /* Set handlers for signals and X errors to perform cleanup */ - - signal(SIGHUP, CleanupSignalHandler); - signal(SIGINT, CleanupSignalHandler); - signal(SIGTERM, CleanupSignalHandler); - defaultXErrorHandler = XSetErrorHandler(CleanupXErrorHandler); - defaultXIOErrorHandler = XSetIOErrorHandler(CleanupXIOErrorHandler); - defaultXtErrorHandler = XtAppSetErrorHandler(appContext, - CleanupXtErrorHandler); + if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), False, GrabModeSync, GrabModeSync, CurrentTime) == GrabSuccess) { + XUngrabKeyboard(dpy, CurrentTime); + } else { + wmState = XInternAtom(dpy, "WM_STATE", False); + if (IconifyNamedWindow(DefaultRootWindow(dpy), "xlogin", False)) { + xloginIconified = True; + XSync(dpy, False); + sleep(1); + } + } + + /* Set handlers for signals and X errors to perform cleanup */ + signal(SIGHUP, CleanupSignalHandler); + signal(SIGINT, CleanupSignalHandler); + signal(SIGTERM, CleanupSignalHandler); + defaultXErrorHandler = XSetErrorHandler(CleanupXErrorHandler); + defaultXIOErrorHandler = XSetIOErrorHandler(CleanupXIOErrorHandler); + defaultXtErrorHandler = XtAppSetErrorHandler(appContext, CleanupXtErrorHandler); } @@ -141,14 +185,22 @@ void ToplevelInitAfterRealization() { - if (appData.fullScreen) { - FullScreenOn(); - } - - wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False); - XSetWMProtocols(dpy, XtWindow(toplevel), &wmDeleteWindow, 1); - XtOverrideTranslations - (toplevel, XtParseTranslationTable ("<Message>WM_PROTOCOLS: Quit()")); + if (appData.fullScreen) { + FullScreenOn(); + if (net_wm_supported()) { + /* problem with scroll bars sticking: */ + XSync(dpy, False); + usleep(50 * 1000); + FullScreenOff(); + XSync(dpy, False); + usleep(50 * 1000); + FullScreenOn(); + } + } + + wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + XSetWMProtocols(dpy, XtWindow(toplevel), &wmDeleteWindow, 1); + XtOverrideTranslations(toplevel, XtParseTranslationTable ("<Message>WM_PROTOCOLS: Quit()")); } @@ -157,9 +209,7 @@ * CurrentTime if the event has no time field. */ -Time -TimeFromEvent(XEvent *ev) -{ +Time TimeFromEvent(XEvent *ev) { switch (ev->type) { case KeyPress: case KeyRelease: @@ -192,18 +242,16 @@ * generated by SendRFBEvent. */ -void -Pause(Widget w, XEvent *event, String *params, Cardinal *num_params) -{ - int msec; +void Pause(Widget w, XEvent *event, String *params, Cardinal *num_params) { + int msec; - if (*num_params == 0) { - msec = 100; - } else { - msec = atoi(params[0]); - } - - usleep(msec * 1000); + if (*num_params == 0) { + msec = 100; + } else { + msec = atoi(params[0]); + } + usleep(msec * 1000); + if (w || event || params || num_params) {} } @@ -256,6 +304,7 @@ /* Wait for Child 1 to die */ wait(&childstatus); + if (w || event || params || num_params) {} return; } @@ -264,11 +313,10 @@ * Quit action - called when we get a "delete window" message. */ -void -Quit(Widget w, XEvent *event, String *params, Cardinal *num_params) -{ - Cleanup(); - exit(0); +void Quit(Widget w, XEvent *event, String *params, Cardinal *num_params) { + Cleanup(); + if (w || event || params || num_params) {} + exit(0); } @@ -276,49 +324,94 @@ * Cleanup - perform any cleanup operations prior to exiting. */ -void -Cleanup() -{ - if (xloginIconified) { - IconifyNamedWindow(DefaultRootWindow(dpy), "xlogin", True); - XFlush(dpy); - } +void Cleanup() { + + if (appData.chatActive) { + appData.chatActive = False; + fprintf(stderr,"Sending SendTextChatClose()\n"); + SendTextChatClose(); + SendTextChatFinished(); + } + + if (xloginIconified) { + IconifyNamedWindow(DefaultRootWindow(dpy), "xlogin", True); + XFlush(dpy); + } #ifdef MITSHM - if (appData.useShm) - ShmCleanup(); + if (appData.useShm) { + if (UsingShm()) { + ShmDetach(); + } + ShmCleanup(); + } #endif + + releaseAllPressedModifiers(); + + fprintf(stderr,"\nVNC Viewer exiting.\n\n"); + if (listenSpecified) { + if (listenParent != 0 && getenv("SSVNC_LISTEN_ONCE") && listenParent != getpid()) { + fprintf(stderr, "SSVNC_LISTEN_ONCE: Trying to kill Listening Parent: %d\n", (int) listenParent); + fprintf(stderr, "SSVNC_LISTEN_ONCE: Press Ctrl-C if it continues to Listen.\n\n"); + kill(listenParent, SIGTERM); + } else { + fprintf(stderr,"(NOTE: You may need to Press Ctrl-C to make the Viewer Stop Listening.)\n\n"); + } + } +} + +static void check_dbg(void) { + if (getenv("SSVNC_EXIT_DEBUG")) { + fprintf(stderr, "Press any key to continue: "); + getc(stdin); + } } static int CleanupXErrorHandler(Display *dpy, XErrorEvent *error) { - fprintf(stderr,"CleanupXErrorHandler called\n"); - Cleanup(); - return (*defaultXErrorHandler)(dpy, error); + if (XError_ign) { + char str[4096]; + XError_ign++; + fprintf(stderr,"XError_ign called.\n"); + str[0] = '\0'; + if (XGetErrorText(dpy, error->error_code, str, 4096)) { + fprintf(stderr, "%s", str); + } + return 0; + } + fprintf(stderr,"CleanupXErrorHandler called\n"); + check_dbg(); + Cleanup(); + return (*defaultXErrorHandler)(dpy, error); } static int CleanupXIOErrorHandler(Display *dpy) { - fprintf(stderr,"CleanupXIOErrorHandler called\n"); - Cleanup(); - return (*defaultXIOErrorHandler)(dpy); + fprintf(stderr,"CleanupXIOErrorHandler called\n"); + check_dbg(); + Cleanup(); + return (*defaultXIOErrorHandler)(dpy); } static void CleanupXtErrorHandler(String message) { - fprintf(stderr,"CleanupXtErrorHandler called\n"); - Cleanup(); - (*defaultXtErrorHandler)(message); + fprintf(stderr,"CleanupXtErrorHandler called\n"); + check_dbg(); + Cleanup(); + (*defaultXtErrorHandler)(message); } static void CleanupSignalHandler(int sig) { - fprintf(stderr,"CleanupSignalHandler called\n"); - Cleanup(); - exit(1); + fprintf(stderr,"CleanupSignalHandler called\n"); + check_dbg(); + Cleanup(); + if (sig) {} + exit(1); } @@ -362,7 +455,7 @@ if (!XQueryTree(dpy, w, &dummy, &dummy, &children, &nchildren)) return False; - for (i = 0; i < nchildren; i++) { + for (i = 0; i < (int) nchildren; i++) { if (IconifyNamedWindow(children[i], name, undo)) { XFree ((char *)children); return True; diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/popup.c vnc_unixsrc/vncviewer/popup.c --- vnc_unixsrc.orig/vncviewer/popup.c 2000-06-11 08:00:53.000000000 -0400 +++ vnc_unixsrc/vncviewer/popup.c 2010-04-11 22:03:32.000000000 -0400 @@ -22,25 +22,69 @@ */ #include "vncviewer.h" +#include <time.h> +#include <sys/wait.h> #include <X11/Xaw/Form.h> #include <X11/Xaw/Command.h> +#include <X11/Xaw/AsciiText.h> #include <X11/Xaw/Toggle.h> +#include <X11/Xaw/Box.h> +#include <X11/Xaw/Scrollbar.h> + Widget popup, fullScreenToggle; +Bool SendTextChatFinished(void); + +void popupFixer(Widget wid) { + Window rr, cr; + unsigned int m; + int x0 = 500, y0 = 500; + int xr, yr, wxr, wyr; + Dimension ph; + if (XQueryPointer(dpy, DefaultRootWindow(dpy), &rr, &cr, &xr, &yr, &wxr, &wyr, &m)) { + x0 = xr; + y0 = yr; + } + XtPopup(wid, XtGrabNone); + XtVaGetValues(wid, XtNheight, &ph, NULL); + if (y0 + (int) ph > dpyHeight) { + y0 = dpyHeight - (int) ph; + if (y0 < 0) { + y0 = 0; + } + } + XtMoveWidget(wid, x0, y0); +} + +void Noop(Widget w, XEvent *event, String *params, Cardinal *num_params) { + if (0) fprintf(stderr, "No-op\n"); + if (w || event || params || num_params) {} +} + void ShowPopup(Widget w, XEvent *event, String *params, Cardinal *num_params) { - XtMoveWidget(popup, event->xbutton.x_root, event->xbutton.y_root); - XtPopup(popup, XtGrabNone); - XSetWMProtocols(dpy, XtWindow(popup), &wmDeleteWindow, 1); + if (appData.popupFix) { + popupFixer(popup); + } else { + XtMoveWidget(popup, event->xbutton.x_root, event->xbutton.y_root); + XtPopup(popup, XtGrabNone); + } + if (appData.grabAll) { + XSync(dpy, False); + XRaiseWindow(dpy, XtWindow(popup)); + } + XSetWMProtocols(dpy, XtWindow(popup), &wmDeleteWindow, 1); + XtOverrideTranslations(popup, XtParseTranslationTable ("<Message>WM_PROTOCOLS: HidePopup()")); + if (w || event || params || num_params) {} } void -HidePopup(Widget w, XEvent *event, String *params, Cardinal *num_params) -{ - XtPopdown(popup); +HidePopup(Widget w, XEvent *event, String *params, Cardinal *num_params) { + XtPopdown(popup); + if (w || event || params || num_params) {} } @@ -52,42 +96,808 @@ }; void -CreatePopup() +CreatePopup() { + Widget buttonForm1, buttonForm2, twoForm, button = 0, prevButton = NULL; + int i; + char buttonName[12]; + String buttonType; + + popup = XtVaCreatePopupShell("popup", transientShellWidgetClass, toplevel, NULL); + + twoForm = XtVaCreateManagedWidget("buttonForm", formWidgetClass, popup, NULL); + buttonForm1 = XtVaCreateManagedWidget("buttonForm", formWidgetClass, twoForm, NULL); + buttonForm2 = XtVaCreateManagedWidget("buttonForm", formWidgetClass, twoForm, XtNfromHoriz, (XtArgVal) buttonForm1, NULL); + + if (appData.popupButtonCount > 100) { + fprintf(stderr,"Too many popup buttons\n"); + exit(1); + } + + for (i = 1; i <= appData.popupButtonCount; i++) { + Widget bform; + sprintf(buttonName, "button%d", i); + + if (i <= appData.popupButtonBreak) { + bform = buttonForm1; + } else { + if (i == appData.popupButtonBreak+1) { + prevButton = NULL; + } + bform = buttonForm2; + } + XtVaGetSubresources(bform, (XtPointer)&buttonType, buttonName, "Button", resources, 1, NULL); + + if (strcmp(buttonType, "command") == 0) { + button = XtVaCreateManagedWidget(buttonName, commandWidgetClass, bform, NULL); + XtVaSetValues(button, XtNfromVert, prevButton, XtNleft, XawChainLeft, XtNright, XawChainRight, NULL); + } else if (strcmp(buttonType, "toggle") == 0) { + button = XtVaCreateManagedWidget(buttonName, toggleWidgetClass, bform, NULL); + XtVaSetValues(button, XtNfromVert, prevButton, XtNleft, XawChainLeft, XtNright, XawChainRight, NULL); + } else { + fprintf(stderr,"unknown button type '%s'\n", buttonType); + } + prevButton = button; + } +} + + +Widget scaleN; + +void +ShowScaleN(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + if (appData.popupFix) { + popupFixer(scaleN); + } else { + XtMoveWidget(scaleN, event->xbutton.x_root, event->xbutton.y_root); + XtPopup(scaleN, XtGrabNone); + } + if (appData.grabAll) { + XRaiseWindow(dpy, XtWindow(scaleN)); + } + XSetWMProtocols(dpy, XtWindow(scaleN), &wmDeleteWindow, 1); + XtOverrideTranslations(scaleN, XtParseTranslationTable ("<Message>WM_PROTOCOLS: HideScaleN()")); + if (w || event || params || num_params) {} +} + +void +HideScaleN(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + XtPopdown(scaleN); + if (w || event || params || num_params) {} +} + + +void +CreateScaleN() { Widget buttonForm, button, prevButton = NULL; int i; - char buttonName[12]; + char buttonName[32]; String buttonType; - popup = XtVaCreatePopupShell("popup", transientShellWidgetClass, toplevel, + scaleN = XtVaCreatePopupShell("scaleN", transientShellWidgetClass, toplevel, NULL); - buttonForm = XtVaCreateManagedWidget("buttonForm", formWidgetClass, popup, + buttonForm = XtVaCreateManagedWidget("buttonForm", formWidgetClass, scaleN, NULL); - if (appData.popupButtonCount > 100) { - fprintf(stderr,"Too many popup buttons\n"); - exit(1); - } - - for (i = 1; i <= appData.popupButtonCount; i++) { + for (i = 0; i <= 6; i++) { sprintf(buttonName, "button%d", i); XtVaGetSubresources(buttonForm, (XtPointer)&buttonType, buttonName, "Button", resources, 1, NULL); - if (strcmp(buttonType, "command") == 0) { - button = XtVaCreateManagedWidget(buttonName, commandWidgetClass, + button = XtVaCreateManagedWidget(buttonName, toggleWidgetClass, buttonForm, NULL); - XtVaSetValues(button, XtNfromVert, prevButton, + XtVaSetValues(button, XtNfromVert, prevButton, XtNleft, XawChainLeft, XtNright, XawChainRight, NULL); - } else if (strcmp(buttonType, "toggle") == 0) { - button = XtVaCreateManagedWidget(buttonName, toggleWidgetClass, + prevButton = button; + } +} + +Widget turbovncW; + +static Widget turboButtons[32]; + +Widget qualtext, qualslider; + +void UpdateQualSlider(void) { +#ifdef TURBOVNC + char text[16]; + XawScrollbarSetThumb(qualslider, (float)appData.qualityLevel/100., 0.); + sprintf(text, "%3d", appData.qualityLevel); + XtVaSetValues(qualtext, XtNlabel, text, NULL); +#endif +} + +void qualScrollProc(Widget w, XtPointer client, XtPointer p) { +#ifdef TURBOVNC + float size, val; int qual, pos=(int)p; + XtVaGetValues(w, XtNshown, &size, XtNtopOfThumb, &val, 0); + if(pos<0) val-=.1; else val+=.1; + qual=(int)(val*100.); if(qual<1) qual=1; if(qual>100) qual=100; + XawScrollbarSetThumb(w, val, 0.); + appData.qualityLevel=qual; + UpdateQual(); +#endif + if (w || client || p) {} +} + +void qualJumpProc(Widget w, XtPointer client, XtPointer p) { +#ifdef TURBOVNC + float val=*(float *)p; int qual; + qual=(int)(val*100.); if(qual<1) qual=1; if(qual>100) qual=100; + appData.qualityLevel=qual; + UpdateQual(); +#endif + if (w || client || p) {} +} + +void UpdateSubsampButtons(void) { +#ifdef TURBOVNC + int i; + for (i=7; i <= 10; i++) { + XtVaSetValues(turboButtons[i], XtNstate, 0, NULL); + } + if (appData.subsampLevel==TVNC_1X) { + i = 7; + } else if (appData.subsampLevel==TVNC_2X) { + i = 8; + } else if (appData.subsampLevel==TVNC_4X) { + i = 9; + } else if (appData.subsampLevel==TVNC_GRAY) { + i = 10; + } else { + return; + } + XtVaSetValues(turboButtons[i], XtNstate, 1, NULL); +#endif +} + +void +ShowTurboVNC(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + UpdateSubsampButtons(); + UpdateQualSlider(); + if (appData.popupFix) { + popupFixer(turbovncW); + } else { + XtMoveWidget(turbovncW, event->xbutton.x_root, event->xbutton.y_root); + XtPopup(turbovncW, XtGrabNone); + } + if (appData.grabAll) { + XRaiseWindow(dpy, XtWindow(turbovncW)); + } + XSetWMProtocols(dpy, XtWindow(turbovncW), &wmDeleteWindow, 1); + XtOverrideTranslations(turbovncW, XtParseTranslationTable ("<Message>WM_PROTOCOLS: HideTurboVNC()")); + if (w || event || params || num_params) {} +} + +void +HideTurboVNC(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + XtPopdown(turbovncW); + if (w || event || params || num_params) {} +} + +void +CreateTurboVNC() { + Widget buttonForm, button, prevButton = NULL; + Widget label; + int i; + char buttonName[32]; + String buttonType; + + turbovncW = XtVaCreatePopupShell("turboVNC", transientShellWidgetClass, toplevel, NULL); + + buttonForm = XtVaCreateManagedWidget("buttonForm", formWidgetClass, turbovncW, NULL); + + for (i = 0; i <= 12; i++) { + sprintf(buttonName, "button%d", i); +#ifndef TURBOVNC + if (i == 0) { + sprintf(buttonName, "buttonNone"); + } else if (i > 0) { + return; + } +#endif + XtVaGetSubresources(buttonForm, (XtPointer)&buttonType, buttonName, + "Button", resources, 1, NULL); + + button = XtVaCreateManagedWidget(buttonName, toggleWidgetClass, + buttonForm, NULL); + turboButtons[i] = button; + XtVaSetValues(button, XtNfromVert, prevButton, + XtNleft, XawChainLeft, XtNright, XawChainRight, NULL); + prevButton = button; + } + + label = XtCreateManagedWidget("qualLabel", toggleWidgetClass, buttonForm, NULL, 0); + XtVaSetValues(label, XtNfromVert, prevButton, XtNleft, XawChainLeft, XtNright, XawChainRight, NULL); + + qualslider = XtCreateManagedWidget("qualBar", scrollbarWidgetClass, buttonForm, NULL, 0); + XtVaSetValues(qualslider, XtNfromVert, label, XtNleft, XawChainLeft, NULL); + XtAddCallback(qualslider, XtNscrollProc, qualScrollProc, NULL) ; + XtAddCallback(qualslider, XtNjumpProc, qualJumpProc, NULL) ; + + qualtext = XtCreateManagedWidget("qualText", labelWidgetClass, buttonForm, NULL, 0); + XtVaSetValues(qualtext, XtNfromVert, label, XtNfromHoriz, qualslider, XtNright, XawChainRight, NULL); +} + +Widget qualityW; + +void +ShowQuality(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + if (appData.popupFix) { + popupFixer(qualityW); + } else { + XtMoveWidget(qualityW, event->xbutton.x_root, event->xbutton.y_root); + XtPopup(qualityW, XtGrabNone); + } + if (appData.grabAll) { + XRaiseWindow(dpy, XtWindow(qualityW)); + } + XSetWMProtocols(dpy, XtWindow(qualityW), &wmDeleteWindow, 1); + XtOverrideTranslations(qualityW, XtParseTranslationTable ("<Message>WM_PROTOCOLS: HideQuality()")); + if (w || event || params || num_params) {} +} + +void +HideQuality(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + XtPopdown(qualityW); + if (w || event || params || num_params) {} +} + + +void +CreateQuality() +{ + Widget buttonForm, button, prevButton = NULL; + int i; + char buttonName[32]; + String buttonType; + + qualityW = XtVaCreatePopupShell("quality", transientShellWidgetClass, toplevel, + NULL); + + buttonForm = XtVaCreateManagedWidget("buttonForm", formWidgetClass, qualityW, + NULL); + + for (i = -1; i <= 9; i++) { + if (i < 0) { + sprintf(buttonName, "buttonD"); + } else { + sprintf(buttonName, "button%d", i); + } + XtVaGetSubresources(buttonForm, (XtPointer)&buttonType, buttonName, + "Button", resources, 1, NULL); + + button = XtVaCreateManagedWidget(buttonName, toggleWidgetClass, buttonForm, NULL); - XtVaSetValues(button, XtNfromVert, prevButton, + XtVaSetValues(button, XtNfromVert, prevButton, XtNleft, XawChainLeft, XtNright, XawChainRight, NULL); + prevButton = button; + } +} + +Widget compressW; + +void +ShowCompress(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + if (appData.popupFix) { + popupFixer(compressW); + } else { + XtMoveWidget(compressW, event->xbutton.x_root, event->xbutton.y_root); + XtPopup(compressW, XtGrabNone); + } + if (appData.grabAll) { + XRaiseWindow(dpy, XtWindow(compressW)); + } + XSetWMProtocols(dpy, XtWindow(compressW), &wmDeleteWindow, 1); + XtOverrideTranslations(compressW, XtParseTranslationTable ("<Message>WM_PROTOCOLS: HideCompress()")); + if (w || event || params || num_params) {} +} + +void +HideCompress(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + XtPopdown(compressW); + if (w || event || params || num_params) {} +} + + +void +CreateCompress() +{ + Widget buttonForm, button, prevButton = NULL; + int i; + char buttonName[32]; + String buttonType; + + compressW = XtVaCreatePopupShell("compress", transientShellWidgetClass, toplevel, + NULL); + + buttonForm = XtVaCreateManagedWidget("buttonForm", formWidgetClass, compressW, + NULL); + + for (i = -1; i <= 9; i++) { + if (i < 0) { + sprintf(buttonName, "buttonD"); } else { - fprintf(stderr,"unknown button type '%s'\n",buttonType); + sprintf(buttonName, "button%d", i); } + XtVaGetSubresources(buttonForm, (XtPointer)&buttonType, buttonName, + "Button", resources, 1, NULL); + + button = XtVaCreateManagedWidget(buttonName, toggleWidgetClass, + buttonForm, NULL); + XtVaSetValues(button, XtNfromVert, prevButton, + XtNleft, XawChainLeft, XtNright, XawChainRight, NULL); prevButton = button; } } + + +int filexfer_sock = -1; +int filexfer_listen = -1; + +void HideFile(Widget w, XEvent *event, String *params, Cardinal *num_params) { + if (filexfer_sock >= 0) { + close(filexfer_sock); + filexfer_sock = -1; + } + if (filexfer_listen >= 0) { + close(filexfer_listen); + filexfer_listen = -1; + } + if (w || event || params || num_params) {} +} + +extern int use_loopback; +time_t start_listen = 0; +pid_t java_helper = 0; + +void ShowFile(Widget w, XEvent *event, String *params, Cardinal *num_params) { + int i, port0 = 7200, port, sock = -1; + char *cmd, *jar; + char fmt[] = "java -cp '%s' VncViewer HOST localhost PORT %d delayAuthPanel yes ignoreMSLogonCheck yes disableSSL yes ftpOnly yes graftFtp yes dsmActive no &"; + + if (getenv("SSVNC_ULTRA_FTP_JAR")) { + jar = getenv("SSVNC_ULTRA_FTP_JAR"); + cmd = (char *) malloc(strlen(fmt) + strlen(jar) + 100); + } else { + fprintf(stderr, "Cannot find UltraVNC FTP jar file.\n"); + return; + } + + use_loopback = 1; + for (i = 0; i < 100; i++) { + port = port0 + i; + sock = ListenAtTcpPort(port); + if (sock < 0) { + sock = ListenAtTcpPort6(port); + } + if (sock >= 0) { + fprintf(stderr, "listening for filexfer on port: %d sock: %d\n", port, sock); + break; + } + } + use_loopback = 0; + + if (sock >= 0) { + int st; + pid_t pid = fork(); + if (pid < 0) { + free(cmd); + return; + } else if (pid == 0) { + int i; + sprintf(cmd, fmt, jar, port); + if (appData.ultraDSM) { + char *q = strstr(cmd, "dsmActive"); + if (q) { + q = strstr(q, "no "); + if (q) { + q[0] = 'y'; + q[1] = 'e'; + q[2] = 's'; + } + } + } + for (i = 3; i < 100; i++) { + close(i); + } + fprintf(stderr, "\n-- Experimental UltraVNC File Transfer --\n\nRunning cmd:\n\n %s\n\n", cmd); + system(cmd); + exit(0); + } + fprintf(stderr, "java helper pid is: %d\n", (int) pid); + waitpid(pid, &st, 0); + java_helper = pid; + start_listen = time(NULL); + } + free(cmd); + filexfer_listen = sock; + if (w || event || params || num_params) {} +} + +Widget chat, entry, text; + +static int chat_visible = 0; + +void +ShowChat(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + if (appData.termChat) { + return; + } + if (! chat_visible) { + XtPopup(chat, XtGrabNone); + chat_visible = 1; + wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + XSetWMProtocols(dpy, XtWindow(chat), &wmDeleteWindow, 1); + if (appData.chatOnly) { + XtOverrideTranslations(chat, XtParseTranslationTable ("<Message>WM_PROTOCOLS: Quit()")); + } else { + XtOverrideTranslations(chat, XtParseTranslationTable ("<Message>WM_PROTOCOLS: HideChat()")); + } + XSync(dpy, False); + usleep(200 * 1000); + } + if (w || event || params || num_params) {} +} + +void hidechat(void) { + appData.chatActive = False; + if (appData.termChat) { + return; + } + if (chat_visible) { + XtPopdown(chat); + chat_visible = 0; + XSync(dpy, False); + usleep(200 * 1000); + } + if (appData.chatOnly) { + Quit(0, NULL, NULL, NULL); + } +} + +void HideChat(Widget w, XEvent *event, String *params, Cardinal *num_params) { + SendTextChatClose(); + SendTextChatFinished(); + hidechat(); + if (w || event || params || num_params) {} +} + +void dismiss_proc(Widget w, XtPointer client_data, XtPointer call_data) { + SendTextChatClose(); + SendTextChatFinished(); + hidechat(); + if (w || client_data || call_data) {} +} + +extern void printChat(char *, Bool); + +static void ChatTextCallback(XtPointer clientData, XtIntervalId *id); +static XtIntervalId timer; +static Bool timerSet = False; + +void CheckTextInput(void); +extern double start_time; + +static void ChatTextCallback(XtPointer clientData, XtIntervalId *id) { + static int db = -1; + if (db < 0) { + if (getenv("SSVNC_DEBUG_CHAT")) { + db = 1; + } else { + db = 0; + } + } + if (db) fprintf(stderr, "ChatTextCallback: %.4f\n", dnow() - start_time); + CheckTextInput(); + if (clientData || id) {} +} + +void CheckTextInput(void) { + Arg args[2]; + String str; + int len; + static int db = -1; + + if (timerSet) { + XtRemoveTimeOut(timer); + timerSet = False; + } + if (appData.chatActive) { + timer = XtAppAddTimeOut(appContext, 333, ChatTextCallback, NULL); + timerSet = True; + } + if (appData.chatOnly && !appData.chatActive) { + Quit(0, NULL, NULL, NULL); + } + + if (appData.termChat) { + return; + } +#if 0 + if (!appData.chatActive) { + return; + } +#endif + + if (db < 0) { + if (getenv("SSVNC_DEBUG_CHAT")) { + db = 1; + } else { + db = 0; + } + } + + XtSetArg(args[0], XtNstring, &str); + XtGetValues(entry, args, 1); + + if (db) fprintf(stderr, "CheckTextInput\n"); + + if (str == NULL || str[0] == '\0') { + return; + } else { + char *q; + len = strlen(str); + if (db) fprintf(stderr, "CheckTextInput: len: %d '%s'\n", len, str); + if (len <= 0) { + return; + } + q = strrchr(str, '\n'); + if (q) { + char *send, save[2]; + save[0] = *(q+1); + *(q+1) = '\0'; + send = strdup(str); + *(q+1) = save[0]; + if (send) { + SendTextChat(send); + printChat("Send: ", True); + printChat(send, True); + free(send); + if (save[0] == '\0') { + XtVaSetValues(entry, XtNtype, XawAsciiString, XtNstring, "", NULL); + } else { + char *leak = strdup(q+1); + XtVaSetValues(entry, XtNtype, XawAsciiString, XtNstring, leak, NULL); + if (strlen(leak) > 0) { + XSync(dpy, False); + XtVaSetValues(entry, XtNinsertPosition, strlen(leak), NULL); + } + } + } + } + } +} + +void AppendChatInput0(char *in) { + Arg args[10]; + int n; + String str; + int len; + static char *s = NULL; + static int slen = -1; + XawTextPosition pos; + + fprintf(stderr, "AppendChatInput: in= '%s'\n", in); + + XtSetArg(args[0], XtNstring, &str); + XtGetValues(text, args, 1); + fprintf(stderr, "AppendChatInput: str='%s'\n", str); + + len = strlen(str) + strlen(in); + + if (slen <= len) { + slen = 2 * (len + 10); + if (s) free(s); + s = (char *) malloc(slen+1); + } + + s[0] = '\0'; + strcat(s, str); + strcat(s, in); + fprintf(stderr, "AppendChatInput s= '%s'\n", s); + pos = (XawTextPosition) (len-1); + n = 0; + XtSetArg(args[n], XtNtype, XawAsciiString); n++; + XtSetArg(args[n], XtNstring, s); n++; + XtSetArg(args[n], XtNdisplayPosition, pos); n++; + XtSetArg(args[n], XtNinsertPosition, pos); n++; + XtSetValues(text, args, n); + fprintf(stderr, "AppendChatInput done\n"); +} + +void AppendChatInput(char *in) { + XawTextPosition beg, end; + static XawTextPosition pos = 0; + XawTextBlock txt; + + if (appData.termChat) { + return; + } + + XawTextSetInsertionPoint(text, pos); + beg = XawTextGetInsertionPoint(text); + end = beg; +#if 0 + fprintf(stderr, "AppendChatInput: pos=%d in= '%s'\n", beg, in); +#endif + + txt.firstPos = 0; + txt.length = strlen(in); + txt.ptr = in; + txt.format = FMT8BIT; + + XawTextReplace(text, beg, end, &txt); + XawTextSetInsertionPoint(text, beg + txt.length); + + pos = XawTextGetInsertionPoint(text); +#if 0 + fprintf(stderr, "AppendChatInput done pos=%d\n", pos); +#endif +} + +#if 0 +static char errorbuf[1] = {0}; +#endif + +void CreateChat(void) { + + Widget myform, dismiss; + Dimension w = 400, h = 300; + + chat = XtVaCreatePopupShell("chat", topLevelShellWidgetClass, toplevel, XtNmappedWhenManaged, False, NULL); + + myform = XtVaCreateManagedWidget("myform", formWidgetClass, chat, NULL); + + text = XtVaCreateManagedWidget("text", asciiTextWidgetClass, myform, + XtNresize, XawtextResizeBoth, XtNresizable, True, XtNwrap, XawtextWrapWord, + XtNscrollHorizontal, XawtextScrollNever, XtNscrollVertical, XawtextScrollAlways, + XtNwidth, w, XtNheight, h, XtNdisplayCaret, False, + XtNeditType, XawtextAppend, XtNtype, XawAsciiString, + XtNuseStringInPlace, False, NULL); + + entry = XtVaCreateManagedWidget("entry", asciiTextWidgetClass, myform, + XtNresize, XawtextResizeWidth, XtNresizable, True, XtNwrap, XawtextWrapNever, + XtNscrollHorizontal, XawtextScrollNever, XtNscrollVertical, XawtextScrollNever, + XtNheight, 20, XtNwidth, 400, XtNfromVert, text, XtNeditType, XawtextEdit, + XtNdisplayCaret, True, XtNeditType, XawtextEdit, NULL); + + dismiss = XtVaCreateManagedWidget("dismiss", commandWidgetClass, myform, XtNlabel, "Close Chat", XtNfromVert, entry, NULL); + + AppendChatInput(""); + + XtAddCallback(dismiss, XtNcallback, dismiss_proc, NULL); + + XtRealizeWidget(chat); + + XtSetKeyboardFocus(chat, entry); +} + +Widget msgwin, msgtext; + +void AppendMsg(char *in) { + XawTextPosition beg, end; + static XawTextPosition pos = 0; + XawTextBlock txt; + + XawTextSetInsertionPoint(msgtext, pos); + beg = XawTextGetInsertionPoint(msgtext); + end = beg; + + txt.firstPos = 0; + txt.length = strlen(in); + txt.ptr = in; + txt.format = FMT8BIT; + + XawTextReplace(msgtext, beg, end, &txt); + XawTextSetInsertionPoint(msgtext, beg + txt.length); + + pos = XawTextGetInsertionPoint(msgtext); +} + +static int msg_visible = 0; +static int msg_NO_clicked = 0; + +void msg_dismiss_proc(Widget w, XtPointer client_data, XtPointer call_data) { + XtPopdown(msgwin); + msg_visible = 0; + XSync(dpy, False); + usleep(200 * 1000); + if (w || client_data || call_data) {} +} + +void msg_NO_proc(Widget w, XtPointer client_data, XtPointer call_data) { + XtPopdown(msgwin); + msg_visible = 0; + msg_NO_clicked = 1; + XSync(dpy, False); + usleep(200 * 1000); + if (w || client_data || call_data) {} +} + +int CreateMsg(char *msg, int wait) { + + Widget myform, dismiss, reject; + char *p; + int n, run, wmax = 0; + int ret = 1; + Dimension w, h; + + + n = 0; + run = 0; + p = msg; + while (*p != '\0') { + if (*p == '\n') { + run = 0; + n++; + } + run++; + if (run > wmax) wmax = run; + p++; + } + if (wmax > 80) { + if (wmax > 120) n++; + if (wmax > 80) n++; + wmax = 80; + } + h = (Dimension) (n+2) * 14; + w = (Dimension) (wmax+10) * 8; + + msgwin = XtVaCreatePopupShell("Message", topLevelShellWidgetClass, toplevel, XtNmappedWhenManaged, False, NULL); + + myform = XtVaCreateManagedWidget("myform", formWidgetClass, msgwin, NULL); + + msgtext = XtVaCreateManagedWidget("msgtext", asciiTextWidgetClass, myform, + XtNresize, XawtextResizeBoth, XtNresizable, True, XtNwrap, XawtextWrapWord, + XtNscrollHorizontal, XawtextScrollNever, XtNscrollVertical, XawtextScrollAlways, + XtNwidth, w, XtNheight, h, XtNdisplayCaret, False, + XtNeditType, XawtextAppend, XtNtype, XawAsciiString, + XtNuseStringInPlace, False, NULL); + + if (wait == 2) { + msg_NO_clicked = 0; + + dismiss = XtVaCreateManagedWidget("dismiss", commandWidgetClass, myform, XtNlabel, "Accept", XtNfromVert, msgtext, NULL); + XtAddCallback(dismiss, XtNcallback, msg_dismiss_proc, NULL); + + reject = XtVaCreateManagedWidget("reject", commandWidgetClass, myform, XtNlabel, "Reject", XtNfromVert, dismiss, NULL); + XtAddCallback(reject, XtNcallback, msg_NO_proc, NULL); + } else { + dismiss = XtVaCreateManagedWidget("dismiss", commandWidgetClass, myform, XtNlabel, "OK", XtNfromVert, msgtext, NULL); + XtAddCallback(dismiss, XtNcallback, msg_dismiss_proc, NULL); + + } + + AppendMsg(""); + AppendMsg(msg); + + XtRealizeWidget(msgwin); + + XtPopup(msgwin, XtGrabNone); + + XSync(dpy, False); + msg_visible = 1; + while (wait && msg_visible) { + if (0) fprintf(stderr, "mv: %d\n", msg_visible); + XtAppProcessEvent(appContext, XtIMAll); + } + if (wait == 2) { + if (msg_NO_clicked) { + ret = 0; + } else { + ret = 1; + } + } + return ret; +} diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/popup_ad vnc_unixsrc/vncviewer/popup_ad --- vnc_unixsrc.orig/vncviewer/popup_ad 1969-12-31 19:00:00.000000000 -0500 +++ vnc_unixsrc/vncviewer/popup_ad 2008-02-17 13:32:34.000000000 -0500 @@ -0,0 +1,20 @@ +#!/usr/bin/perl + +$ok = 0; + +open(A, "<argsresources.c") || die; + +while (<A>) { + if (/popupButtonCount:/) { + $on = 1; + } elsif (/^\s*NULL/) { + $on = 0; + } + next unless $on; + chomp; + last if /NULL/; + $_ =~ s/^\s*"//; + $_ =~ s/",//; + $_ .= "\n" unless $_ =~ /\n/; + print; +} diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/rfbproto.c vnc_unixsrc/vncviewer/rfbproto.c --- vnc_unixsrc.orig/vncviewer/rfbproto.c 2008-09-05 19:51:24.000000000 -0400 +++ vnc_unixsrc/vncviewer/rfbproto.c 2010-04-17 22:34:38.000000000 -0400 @@ -23,7 +23,10 @@ * rfbproto.c - functions to deal with client side of RFB protocol. */ +#include <sys/stat.h> #include <unistd.h> +#include <time.h> +#include <ctype.h> #include <errno.h> #include <pwd.h> #include <vncviewer.h> @@ -31,6 +34,9 @@ #include <zlib.h> #include <jpeglib.h> +int server_major = 0, server_minor = 0; +int viewer_major = 0, viewer_minor = 0; + static void InitCapabilities(void); static Bool SetupTunneling(void); static int ReadSecurityType(void); @@ -57,6 +63,47 @@ static Bool HandleTight16(int rx, int ry, int rw, int rh); static Bool HandleTight32(int rx, int ry, int rw, int rh); +/* runge add zrle */ +static Bool HandleZRLE8(int rx, int ry, int rw, int rh); +static Bool HandleZRLE15(int rx, int ry, int rw, int rh); +static Bool HandleZRLE16(int rx, int ry, int rw, int rh); +static Bool HandleZRLE24(int rx, int ry, int rw, int rh); +static Bool HandleZRLE24Up(int rx, int ry, int rw, int rh); +static Bool HandleZRLE24Down(int rx, int ry, int rw, int rh); +static Bool HandleZRLE32(int rx, int ry, int rw, int rh); + +extern Bool HandleCursorPos(int x, int y); +extern void printChat(char *, Bool); + +typedef struct { + unsigned long length; +} rfbZRLEHeader; + +#define sz_rfbZRLEHeader 4 + +#define rfbZRLETileWidth 64 +#define rfbZRLETileHeight 64 + +#define DO_ZYWRLE 1 + +#if DO_ZYWRLE + +#ifndef ZRLE_ONCE +#define ZRLE_ONCE + +static const int bitsPerPackedPixel[] = { + 0, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 +}; + +int zywrle_level; +int zywrleBuf[rfbZRLETileWidth*rfbZRLETileHeight]; + +#include "zrlepalettehelper.h" +static zrlePaletteHelper paletteHelper; + +#endif /* ZRLE_ONCE */ +#endif /* DO_ZYWRLE */ + static void ReadConnFailedReason(void); static long ReadCompactLen (void); @@ -67,6 +114,25 @@ static void JpegSetSrcManager(j_decompress_ptr cinfo, CARD8 *compressedData, int compressedLen); +extern void deskey(unsigned char *, int); +extern void des(unsigned char *, unsigned char *); + +extern int currentMsg; +extern double scale_factor_x; +extern double scale_factor_y; + +extern int skip_maybe_sync; + +int sent_FBU = 0; +int skip_XtUpdate = 0; +int skip_XtUpdateAll = 0; + +static double dt_out = 0.0; +static double dt_out_sc = 0.0; +double latency = 0.0; +double connect_time = 0.0; + +void raiseme(int force); int rfbsock; char *desktopName; @@ -75,6 +141,14 @@ char *serverCutText = NULL; Bool newServerCutText = False; +/* ultravnc mslogon */ +#define rfbUltraVncMsLogon 0xfffffffa +static Bool AuthUltraVncMsLogon(void); +extern void UvncEncryptPasswd_MSLOGON(unsigned char *encryptedPasswd, char *passwd); +extern void UvncEncryptBytes2(unsigned char *where, int length, unsigned char *key); +extern void UvncDecryptBytes2(unsigned char *where, int length, unsigned char *key); +extern unsigned int urandom(void); + int endianTest = 1; static Bool tightVncProtocol = False; @@ -177,8 +251,26 @@ sig_rfbEncodingPointerPos, "Pointer position update"); CapsAdd(encodingCaps, rfbEncodingLastRect, rfbTightVncVendor, sig_rfbEncodingLastRect, "LastRect protocol extension"); + + CapsAdd(encodingCaps, rfbEncodingNewFBSize, rfbTightVncVendor, + sig_rfbEncodingNewFBSize, "New FB size protocol extension"); + +#ifdef TURBOVNC + CapsAdd(encodingCaps, rfbJpegQualityLevel1, rfbTurboVncVendor, + sig_rfbEncodingNewFBSize, "TurboJPEG quality level"); + CapsAdd(encodingCaps, rfbJpegSubsamp1X, rfbTurboVncVendor, + sig_rfbEncodingNewFBSize, "TurboJPEG subsampling level"); +#endif } +static char msgbuf[10000]; + +static void wmsg(char *msg, int wait) { + fprintf(stderr, "%s", msg); + if (!use_tty() && !getenv("SSVNC_NO_MESSAGE_POPUP")) { + CreateMsg(msg, wait); + } +} /* * ConnectToRFBServer. @@ -187,24 +279,179 @@ Bool ConnectToRFBServer(const char *hostname, int port) { - unsigned int host; - - if (!StringToIPAddr(hostname, &host)) { - fprintf(stderr,"Couldn't convert '%s' to host address\n", hostname); - return False; - } + char *q, *cmd = NULL; + Bool setnb; + struct stat sb; + + if (strstr(hostname, "exec=") == hostname) { + cmd = strdup(hostname); + q = strchr(cmd, '='); + *q = ' '; + if (getenv("SSVNC_BASEDIR")) { + char *base = getenv("SSVNC_BASEDIR"); + char *newcmd = (char *)malloc(strlen(base) + strlen(cmd) + 1000); + sprintf(newcmd, "%s/unwrap.so", base); + if (stat(newcmd, &sb) == 0) { +#if (defined(__MACH__) && defined(__APPLE__)) + sprintf(newcmd, "DYLD_FORCE_FLAT_NAMESPACE=1; export DYLD_FORCE_FLAT_NAMESPACE; DYLD_INSERT_LIBRARIES='%s/unwrap.so'; export DYLD_INSERT_LIBRARIES; %s", base, cmd); +#else + sprintf(newcmd, "LD_PRELOAD='%s/unwrap.so'; export LD_PRELOAD; %s", base, cmd); +#endif + cmd = newcmd; + } + } + } - rfbsock = ConnectToTcpAddr(host, port); + if (cmd != NULL) { + int sfd[2]; + char *q, *cmd2 = strdup(cmd); + pid_t pid; + + q = strstr(cmd2, "pw="); + if (q && !getenv("SSVNC_SHOW_ULTRAVNC_DSM_PASSWORD")) { + q += strlen("pw="); + while (*q != '\0' && !isspace(*q)) { + *q = '*'; + q++; + } + } + + fprintf(stderr, "exec-cmd: %s\n\n", cmd2); + free(cmd2); + + if (! SocketPair(sfd)) { + return False; + } + if (0) { + fprintf(stderr, "sfd: %d %d\n", sfd[0], sfd[1]); + fflush(stderr); + } + + pid = fork(); + if (pid == -1) { + perror("fork"); + return False; + } + if (pid == 0) { + char *args[4]; + int d; + args[0] = "/bin/sh"; + args[1] = "-c"; + args[2] = cmd; + args[3] = NULL; + + close(sfd[1]); + dup2(sfd[0], 0); + dup2(sfd[0], 1); + for (d=3; d < 256; d++) { + if (d != sfd[0]) { + close(d); + } + } + execvp(args[0], args); + perror("exec"); + exit(1); + } else { + close(sfd[0]); + rfbsock = sfd[1]; + } + if (rfbsock < 0) { + sprintf(msgbuf,"Unable to connect to exec'd command: %s\n", cmd); + wmsg(msgbuf, 1); + return False; + } + } else if (strstr(hostname, "fd=") == hostname) { + rfbsock = atoi(hostname + strlen("fd=")); + } else if (strchr(hostname, '/') && stat(hostname, &sb) == 0) { + /* assume unix domain socket */ + char *thost = strdup(hostname); + + rfbsock = ConnectToUnixSocket(thost); + free(thost); + + if (rfbsock < 0) { + sprintf(msgbuf,"Unable to connect to VNC server (unix-domain socket: %s)\n", hostname); + wmsg(msgbuf, 1); + return False; + } + + } else { + rfbsock = ConnectToTcpAddr(hostname, port); + + if (rfbsock < 0 && !appData.noipv4) { + char *q, *hosttmp; + if (hostname[0] == '[') { + hosttmp = strdup(hostname+1); + } else { + hosttmp = strdup(hostname); + } + q = strrchr(hosttmp, ']'); + if (q) *q = '\0'; + if (strstr(hosttmp, "::ffff:") == hosttmp || strstr(hosttmp, "::FFFF:") == hosttmp) { + char *host = hosttmp + strlen("::ffff:"); + if (dotted_ip(host, 0)) { + fprintf(stderr, "ConnectToTcpAddr[ipv4]: re-trying connection using '%s'\n", host); + rfbsock = ConnectToTcpAddr(host, port); + } + } + free(hosttmp); + } + + if (rfbsock < 0) { + sprintf(msgbuf,"Unable to connect to VNC server (%s:%d)\n", hostname, port); + wmsg(msgbuf, 1); + return False; + } + } - if (rfbsock < 0) { - fprintf(stderr,"Unable to connect to VNC server\n"); - return False; - } + setnb = SetNonBlocking(rfbsock); + return setnb; +} - return SetNonBlocking(rfbsock); +static void printFailureReason(void) { + CARD32 reasonLen; + ReadFromRFBServer((char *)&reasonLen, 4); + reasonLen = Swap32IfLE(reasonLen); + if (reasonLen < 4096) { + char *reason = (char *) malloc(reasonLen+1); + memset(reason, 0, reasonLen+1); + ReadFromRFBServer(reason, reasonLen); + sprintf(msgbuf, "Reason: %s\n", reason); + wmsg(msgbuf, 1); + free(reason); + } } +static char *pr_sec_type(int type) { + char *str = "unknown"; + if (type == rfbSecTypeInvalid) str = "rfbSecTypeInvalid"; + if (type == rfbSecTypeNone) str = "rfbSecTypeNone"; + if (type == rfbSecTypeVncAuth) str = "rfbSecTypeVncAuth"; + if (type == rfbSecTypeRA2) str = "rfbSecTypeRA2"; + if (type == rfbSecTypeRA2ne) str = "rfbSecTypeRA2ne"; + if (type == rfbSecTypeTight) str = "rfbSecTypeTight"; + if (type == rfbSecTypeUltra) str = "rfbSecTypeUltra"; + + if (type == rfbSecTypeAnonTls) str = "rfbSecTypeAnonTls"; + if (type == rfbSecTypeVencrypt) str = "rfbSecTypeVencrypt"; + + if (type == (int) rfbUltraVncMsLogon) str = "rfbUltraVncMsLogon"; + return str; +} + +static char *pr_sec_subtype(int type) { + char *str = "unknown"; + if (type == rfbVencryptPlain) str = "rfbVencryptPlain"; + if (type == rfbVencryptTlsNone) str = "rfbVencryptTlsNone"; + if (type == rfbVencryptTlsVnc) str = "rfbVencryptTlsVnc"; + if (type == rfbVencryptTlsPlain) str = "rfbVencryptTlsPlain"; + if (type == rfbVencryptX509None) str = "rfbVencryptX509None"; + if (type == rfbVencryptX509Vnc) str = "rfbVencryptX509Vnc"; + if (type == rfbVencryptX509Plain) str = "rfbVencryptX509Plain"; + return str; +} +extern void ProcessXtEvents(void); /* * InitialiseRFBConnection. */ @@ -212,211 +459,654 @@ Bool InitialiseRFBConnection(void) { - rfbProtocolVersionMsg pv; - int server_major, server_minor; - int viewer_major, viewer_minor; - rfbClientInitMsg ci; - int secType; + rfbProtocolVersionMsg pv; + rfbClientInitMsg ci; + int i, secType, anon_dh = 0, accept_uvnc = 0; + FILE *pd; + char *hsfile = NULL; + char *hsparam[128]; + char *envsetsec = getenv("SSVNC_SET_SECURITY_TYPE"); + char line[128]; + double dt = 0.0; - /* if the connection is immediately closed, don't report anything, so - that pmw's monitor can make test connections */ + /* if the connection is immediately closed, don't report anything, so + that pmw's monitor can make test connections */ - if (listenSpecified) - errorMessageOnReadFailure = False; + if (listenSpecified) { + errorMessageOnReadFailure = False; + } - if (!ReadFromRFBServer(pv, sz_rfbProtocolVersionMsg)) - return False; + for (i=0; i < 128; i++) { + hsparam[i] = NULL; + } - errorMessageOnReadFailure = True; + skip_XtUpdateAll = 1; + ProcessXtEvents(); + skip_XtUpdateAll = 0; + + if (getenv("SSVNC_PREDIGESTED_HANDSHAKE")) { + double start = dnow(); + hsfile = getenv("SSVNC_PREDIGESTED_HANDSHAKE"); + while (dnow() < start + 10.0) { + int done = 0; + usleep(100 * 1000); + if ((pd = fopen(hsfile, "r")) != NULL) { + while (fgets(line, 128, pd) != NULL) { + if (strstr(line, "done") == line) { + done = 1; + usleep(100 * 1000); + break; + } + } + fclose(pd); + } + if (done) { + break; + } + } + if ((pd = fopen(hsfile, "r")) != NULL) { + i = 0; + while (fgets(line, 128, pd) != NULL) { + hsparam[i] = strdup(line); + fprintf(stderr, "%s", line); + if (i++ > 100) break; + } + fclose(pd); + } + unlink(hsfile); + } - pv[sz_rfbProtocolVersionMsg] = 0; + if (getenv("SSVNC_SKIP_RFB_PROTOCOL_VERSION")) { + viewer_major = 3; + viewer_minor = 8; + goto end_of_proto_msg; + } else if (hsfile) { + int k = 0; + while (hsparam[k] != NULL) { + char *str = hsparam[k++]; + if (strstr(str, "server=") == str) { + sprintf(pv, "%s", str + strlen("server=")); + goto readed_pv; + } + } + } - if (sscanf(pv, rfbProtocolVersionFormat, - &server_major, &server_minor) != 2) { - fprintf(stderr,"Not a valid VNC server\n"); - return False; - } + dt = dnow(); + if (!ReadFromRFBServer(pv, sz_rfbProtocolVersionMsg)) { + return False; + } + if (getenv("PRINT_DELAY1")) fprintf(stderr, "delay1: %.3f ms\n", (dnow() - dt) * 1000); + dt = 0.0; - viewer_major = rfbProtocolMajorVersion; - if (server_major == 3 && server_minor >= rfbProtocolMinorVersion) { - /* the server supports at least the standard protocol 3.7 */ - viewer_minor = rfbProtocolMinorVersion; - } else { - /* any other server version, request the standard 3.3 */ - viewer_minor = rfbProtocolFallbackMinorVersion; - } + readed_pv: - fprintf(stderr, "Connected to RFB server, using protocol version %d.%d\n", - viewer_major, viewer_minor); + errorMessageOnReadFailure = True; - sprintf(pv, rfbProtocolVersionFormat, viewer_major, viewer_minor); + pv[sz_rfbProtocolVersionMsg] = 0; - if (!WriteExact(rfbsock, pv, sz_rfbProtocolVersionMsg)) - return False; + if (strstr(pv, "ID:") == pv) { + ; + } else if (sscanf(pv, rfbProtocolVersionFormat, &server_major, &server_minor) != 2) { + if (strstr(pv, "test") == pv) { + /* now some hacks for ultraVNC SC III (SSL) ... testA, etc */ + int i; + char *se = NULL; + + fprintf(stderr,"Trying UltraVNC Single Click III workaround: %s\n", pv); + for (i=0; i < 7 ; i++) { + pv[i] = pv[i+5]; + } + if (!ReadFromRFBServer(pv+7, 5)) { + return False; + } + + se = getenv("STUNNEL_EXTRA_OPTS"); + if (se == NULL) { + se = getenv("STUNNEL_EXTRA_OPTS_USER"); + } + if (se != NULL) { + if (strstr(se, "options")) { + if (strstr(se, "ALL") || strstr(se, "DONT_INSERT_EMPTY_FRAGMENTS")) { + ; /* good */ + } else { + se = NULL; + } + } else { + se = NULL; + } + } + if (se == NULL) { + msgbuf[0] = '\0'; + strcat(msgbuf, "\n"); + strcat(msgbuf, "***************************************************************\n"); + strcat(msgbuf, "To work around UltraVNC SC III SSL dropping after a few minutes\n"); + strcat(msgbuf, "you may need to set STUNNEL_EXTRA_OPTS_USER='options = ALL'.\n"); + strcat(msgbuf, "***************************************************************\n"); + strcat(msgbuf, "\n"); + wmsg(msgbuf, 0); + } + if (strstr(pv, "ID:") == pv) { + goto check_ID_string; + } + if (sscanf(pv, rfbProtocolVersionFormat, &server_major, &server_minor) == 2) { + goto ultra_vnc_nonsense; + } + } + sprintf(msgbuf, "Not a valid VNC server: '%s'\n", pv); + wmsg(msgbuf, 1); + return False; + } - /* Read or select the security type. */ - if (viewer_minor == rfbProtocolMinorVersion) { - secType = SelectSecurityType(); - } else { - secType = ReadSecurityType(); - } - if (secType == rfbSecTypeInvalid) - return False; + check_ID_string: + if (strstr(pv, "ID:") == pv) { + char tmp[256]; + fprintf(stderr, "UltraVNC Repeater string detected: %s\n", pv); + fprintf(stderr, "Pretending to be UltraVNC repeater: reading 250 bytes...\n\n"); + if (!ReadFromRFBServer(tmp, 250 - 12)) { + return False; + } + if (!ReadFromRFBServer(pv, 12)) { + return False; + } + if (sscanf(pv, rfbProtocolVersionFormat, &server_major, &server_minor) != 2) { + sprintf(msgbuf,"Not a valid VNC server: '%s'\n", pv); + wmsg(msgbuf, 1); + return False; + } + } - switch (secType) { - case rfbSecTypeNone: - fprintf(stderr, "No authentication needed\n"); - break; - case rfbSecTypeVncAuth: - if (!AuthenticateVNC()) - return False; - break; - case rfbSecTypeTight: - tightVncProtocol = True; - InitCapabilities(); - if (!SetupTunneling()) - return False; - if (!PerformAuthenticationTight()) - return False; - break; - default: /* should never happen */ - fprintf(stderr, "Internal error: Invalid security type\n"); - return False; - } + ultra_vnc_nonsense: + fprintf(stderr,"\nProto: %s\n", pv); - ci.shared = (appData.shareDesktop ? 1 : 0); + viewer_major = 3; - if (!WriteExact(rfbsock, (char *)&ci, sz_rfbClientInitMsg)) - return False; + if (appData.rfbVersion != NULL && sscanf(appData.rfbVersion, "%d.%d", &viewer_major, &viewer_minor) == 2) { + fprintf(stderr,"Setting RFB version to %d.%d from -rfbversion.\n\n", viewer_major, viewer_minor); - if (!ReadFromRFBServer((char *)&si, sz_rfbServerInitMsg)) - return False; + } else if (getenv("SSVNC_RFB_VERSION") != NULL && sscanf(getenv("SSVNC_RFB_VERSION"), "%d.%d", &viewer_major, &viewer_minor) == 2) { + fprintf(stderr,"Setting RFB version to %d.%d from SSVNC_RFB_VERSION.\n\n", viewer_major, viewer_minor); + + } else if (server_major > 3) { + viewer_minor = 8; + } else if (server_major == 3 && (server_minor == 14 || server_minor == 16)) { + /* hack for UltraVNC Single Click. They misuse rfb proto version */ + fprintf(stderr,"Setting RFB version to 3.3 for UltraVNC Single Click.\n\n"); + viewer_minor = 3; + + } else if (server_major == 3 && server_minor >= 8) { + /* the server supports at least the standard protocol 3.8 */ + viewer_minor = 8; + + } else if (server_major == 3 && server_minor == 7) { + /* the server supports at least the standard protocol 3.7 */ + viewer_minor = 7; + + } else { + /* any other server version, request the standard 3.3 */ + viewer_minor = 3; + } + /* n.b. Apple Remote Desktop uses 003.889, but we should be OK with 3.8 */ - si.framebufferWidth = Swap16IfLE(si.framebufferWidth); - si.framebufferHeight = Swap16IfLE(si.framebufferHeight); - si.format.redMax = Swap16IfLE(si.format.redMax); - si.format.greenMax = Swap16IfLE(si.format.greenMax); - si.format.blueMax = Swap16IfLE(si.format.blueMax); - si.nameLength = Swap32IfLE(si.nameLength); - - /* FIXME: Check arguments to malloc() calls. */ - desktopName = malloc(si.nameLength + 1); - if (!desktopName) { - fprintf(stderr, "Error allocating memory for desktop name, %lu bytes\n", - (unsigned long)si.nameLength); - return False; - } + if (appData.msLogon) { + if (server_minor == 4) { + fprintf(stderr,"Setting RFB version to 3.4 for UltraVNC MS Logon.\n\n"); + viewer_minor = 4; + } + } + if (getenv("SSVNC_ACCEPT_POPUP_SC")) { + if (server_minor == -4 || server_minor == -6 || server_minor == 14 || server_minor == 16) { + /* 4 and 6 work too? */ + viewer_minor = server_minor; + accept_uvnc = 1; + fprintf(stderr,"Reset RFB version to 3.%d for UltraVNC SSVNC_ACCEPT_POPUP_SC.\n\n", viewer_minor); + } + } - if (!ReadFromRFBServer(desktopName, si.nameLength)) return False; + fprintf(stderr, "Connected to RFB server, using protocol version %d.%d\n", viewer_major, viewer_minor); - desktopName[si.nameLength] = 0; + if (hsfile) { + int k = 0; + while (hsparam[k] != NULL) { + char *str = hsparam[k++]; + if (strstr(str, "latency=") == str) { + latency = 1000. * atof(str + strlen("latency=")); + } + } + k = 0; + while (hsparam[k] != NULL) { + char *str = hsparam[k++]; + int v1, v2; + if (sscanf(str, "viewer=RFB %d.%d\n", &v1, &v2) == 2) { + viewer_major = v1; + viewer_minor = v2; + fprintf(stderr, "\nPre-Handshake set protocol version to: %d.%d Latency: %.2f ms\n", viewer_major, viewer_minor, latency); + goto end_of_proto_msg; + } + } + } + sprintf(pv, rfbProtocolVersionFormat, viewer_major, viewer_minor); - fprintf(stderr,"Desktop name \"%s\"\n",desktopName); + if (!appData.appShare) { + usleep(100*1000); + } + dt = dnow(); + if (!WriteExact(rfbsock, pv, sz_rfbProtocolVersionMsg)) { + return False; + } - fprintf(stderr,"VNC server default format:\n"); - PrintPixelFormat(&si.format); + end_of_proto_msg: - if (tightVncProtocol) { - /* Read interaction capabilities (protocol 3.7t) */ - if (!ReadInteractionCaps()) - return False; - } + if (envsetsec) { + secType = atoi(getenv("SSVNC_SET_SECURITY_TYPE")); + goto sec_type; + } + if (hsfile) { + int k = 0; + while (hsparam[k] != NULL) { + char *str = hsparam[k++]; + int st; + if (sscanf(str, "sectype=%d\n", &st) == 1) { + secType = st; + fprintf(stderr, "Pre-Handshake set Security-Type to: %d (%s)\n", st, pr_sec_type(st)); + if (secType == rfbSecTypeVencrypt) { + goto sec_type; + } else if (secType == rfbSecTypeAnonTls) { + break; + } + } + } + } - return True; + if (accept_uvnc) { + unsigned int msg_sz = 0; + unsigned int nimmer = 0; + char msg[3000]; + char *msg_buf, *sip = NULL, *sih = NULL; + + if (!ReadFromRFBServer((char *) &msg_sz, 4)) { + return False; + } + dt_out_sc = dnow(); + msg_sz = Swap32IfBE(msg_sz); + if (msg_sz > 1024) { + fprintf(stderr, "UVNC msg size too big: %d\n", msg_sz); + exit(1); + } + msg_buf = (char *)calloc(msg_sz + 100, 1); + if (!ReadFromRFBServer(msg_buf, msg_sz)) { + return False; + } + + if (0) { + fprintf(stderr, "msg_buf: "); + write(2, msg_buf, msg_sz); + fprintf(stderr, "\n"); + } + + sip = get_peer_ip(rfbsock); + if (strlen(sip) > 100) sip = "0.0.0.0"; + sih = ip2host(sip); + if (strlen(sih) > 300) sih = "unknown"; + + sprintf(msg, "\n(LISTEN) Reverse VNC connection from IP: %s\n Hostname: %s\n\n", sip, sih); + strcat(msg, "UltraVNC Server Message:\n"); + strcat(msg, msg_buf); + free(msg_buf); + strcat(msg, "\n\n"); + strcat(msg, "Accept or Reject VNC connection?"); + if (CreateMsg(msg, 2)) { + nimmer = 1; + fprintf(stderr, "Accepting connection.\n\n"); + } else { + nimmer = 0; + fprintf(stderr, "Refusing connection.\n\n"); + } + if (!WriteExact(rfbsock, (char *) &nimmer, 4)) { + return False; + } + } + + /* Read or select the security type. */ + dt_out = 0.0; + + skip_XtUpdateAll = 1; + if (viewer_minor >= 7 && !accept_uvnc) { + secType = SelectSecurityType(); + } else { + secType = ReadSecurityType(); + } + skip_XtUpdateAll = 0; + + if (accept_uvnc) { + dt_out = dt_out_sc; + } + + if (dt > 0.0 && dt_out > dt) { + latency = (dt_out - dt) * 1000; + } + + fprintf(stderr, "Security-Type: %d (%s) Latency: %.2f ms\n", (int) secType, pr_sec_type(secType), latency); + if (secType == rfbSecTypeInvalid) { + return False; + } + + sec_type: + + if (hsfile) { + int subsectype = 0; + int k = 0; + while (hsparam[k] != NULL) { + char *str = hsparam[k++]; + int st; + if (sscanf(str, "subtype=%d\n", &st) == 1) { + subsectype = st; + fprintf(stderr, "Pre-Handshake set Sub-Security-Type to: %d (%s)\n\n", st, pr_sec_subtype(st)); + break; + } + } + + if (!subsectype) { + ; + } else if (secType == rfbSecTypeVencrypt) { + if (subsectype == rfbVencryptTlsNone) { + anon_dh = 1; + secType = rfbSecTypeNone; + } else if (subsectype == rfbVencryptTlsVnc) { + anon_dh = 1; + secType = rfbSecTypeVncAuth; + } else if (subsectype == rfbVencryptTlsPlain) { + anon_dh = 1; + secType = rfbSecTypeNone; + } else if (subsectype == rfbVencryptX509None) { + secType = rfbSecTypeNone; + } else if (subsectype == rfbVencryptX509Vnc) { + secType = rfbSecTypeVncAuth; + } else if (subsectype == rfbVencryptX509Plain) { + secType = rfbSecTypeNone; + } + if (subsectype == rfbVencryptTlsPlain || subsectype == rfbVencryptX509Plain) { + usleep(300*1000); + } + if (subsectype == rfbVencryptTlsNone || subsectype == rfbVencryptTlsVnc || subsectype == rfbVencryptTlsPlain) { + char tmp[1000], line[100]; + tmp[0] = '\0'; + strcat(tmp, "\n"); + sprintf(line, "WARNING: Anonymous Diffie-Hellman TLS used (%s),\n", pr_sec_subtype(subsectype)); + strcat(tmp, line); + strcat(tmp, "WARNING: there will be *NO* Authentication of the VNC Server.\n"); + strcat(tmp, "WARNING: I.e. a Man-In-The-Middle attack is possible.\n"); + strcat(tmp, "WARNING: Configure the server to use X509 certs and verify them.\n\n"); + wmsg(tmp, 1); + } + if (subsectype == rfbVencryptTlsPlain || subsectype == rfbVencryptX509Plain) { + fprintf(stderr, "\nVeNCrypt Plain (username + passwd) selected.\n\n"); + if (appData.unixPW != NULL) { + unixpw(appData.unixPW, 1); + } else if (getenv("SSVNC_UNIXPW")) { + unixpw(getenv("SSVNC_UNIXPW"), 1); + } else { + unixpw(".", 1); + } + } + } + } + + switch (secType) { + case rfbSecTypeNone: + fprintf(stderr, "No VNC authentication needed\n"); + if (viewer_minor >= 8) { + CARD32 authResult; + + if (!ReadFromRFBServer((char *)&authResult, 4)) { + return False; + } + + authResult = Swap32IfLE(authResult); + + if (authResult == rfbVncAuthOK) { + fprintf(stderr, "VNC authentication succeeded (%d) for rfbSecTypeNone (RFB 3.8)\n", (int) authResult); + } else { + sprintf(msgbuf, "VNC authentication failed (%d) for rfbSecTypeNone (RFB 3.8)\n\n", (int) authResult); + wmsg(msgbuf, 1); + return False; + } + } + fprintf(stderr, "\n"); + break; + case rfbSecTypeVncAuth: + if (!AuthenticateVNC()) { + return False; + } + break; + case rfbSecTypeTight: + tightVncProtocol = True; + InitCapabilities(); + if (!SetupTunneling()) { + return False; + } + if (!PerformAuthenticationTight()) { + return False; + } + break; + case rfbUltraVncMsLogon: + if (!AuthUltraVncMsLogon()) { + return False; + } + break; + default: /* should never happen */ + sprintf(msgbuf, "Internal error: Invalid security type: %d\n", secType); + wmsg(msgbuf, 1); + return False; + } + + connect_time = dnow(); + + ci.shared = (appData.shareDesktop ? 1 : 0); + + if (!WriteExact(rfbsock, (char *)&ci, sz_rfbClientInitMsg)) { + return False; + } + + if (!ReadFromRFBServer((char *)&si, sz_rfbServerInitMsg)) { + return False; + } + + si.framebufferWidth = Swap16IfLE(si.framebufferWidth); + si.framebufferHeight = Swap16IfLE(si.framebufferHeight); + si.format.redMax = Swap16IfLE(si.format.redMax); + si.format.greenMax = Swap16IfLE(si.format.greenMax); + si.format.blueMax = Swap16IfLE(si.format.blueMax); + si.nameLength = Swap32IfLE(si.nameLength); + + if (appData.chatOnly) { + si.framebufferWidth = 32; + si.framebufferHeight = 32; + } + + /* FIXME: Check arguments to malloc() calls. */ + desktopName = malloc(si.nameLength + 1); + memset(desktopName, 0, si.nameLength + 1); + if (!desktopName) { + fprintf(stderr, "Error allocating memory for desktop name, %lu bytes\n", + (unsigned long)si.nameLength); + return False; + } + + if (!ReadFromRFBServer(desktopName, si.nameLength)) { + return False; + } + + desktopName[si.nameLength] = 0; + + if (appData.appShare) { + int x_hint, y_hint; + char *p, *q = NULL; + p = desktopName; + while (*p != '\0') { + char *t = strstr(p, " XY="); + if (t) q = t; + p++; + } + if (q) { + int ok = 1; + p = q + strlen(" XY="); + while (*p != '\0') { + if (!strpbrk(p, "0123456789,+-")) { + ok = 0; + } + p++; + } + if (ok && sscanf(q+1, "XY=%d,%d", &x_hint, &y_hint) == 2) { + fprintf(stderr,"Using x11vnc appshare position: %s\n\n", q); + *q = '\0'; + appshare_x_hint = x_hint; + appshare_y_hint = y_hint; + } + } + } + + fprintf(stderr,"Desktop name \"%s\"\n\n", desktopName); + + fprintf(stderr,"VNC server default format:\n"); + PrintPixelFormat(&si.format); + + if (tightVncProtocol) { + /* Read interaction capabilities (protocol 3.7t) */ + if (!ReadInteractionCaps()) { + return False; + } + } + + return True; } /* - * Read security type from the server (protocol version 3.3) + * Read security type from the server (protocol 3.3) */ static int ReadSecurityType(void) { - CARD32 secType; + CARD32 secType; - /* Read the security type */ - if (!ReadFromRFBServer((char *)&secType, sizeof(secType))) - return rfbSecTypeInvalid; + /* Read the security type */ + if (!ReadFromRFBServer((char *)&secType, sizeof(secType))) { + return rfbSecTypeInvalid; + } + dt_out = dnow(); - secType = Swap32IfLE(secType); + secType = Swap32IfLE(secType); - if (secType == rfbSecTypeInvalid) { - ReadConnFailedReason(); - return rfbSecTypeInvalid; - } + if (secType == rfbSecTypeInvalid) { + ReadConnFailedReason(); + return rfbSecTypeInvalid; + } - if (secType != rfbSecTypeNone && secType != rfbSecTypeVncAuth) { - fprintf(stderr, "Unknown security type from RFB server: %d\n", - (int)secType); - return rfbSecTypeInvalid; - } + if (secType == rfbSecTypeNone) { + ; /* OK */ + } else if (secType == rfbSecTypeVncAuth) { + ; /* OK */ + } else if (secType == rfbUltraVncMsLogon) { + ; /* OK */ + } else { + sprintf(msgbuf, "Unknown security type from RFB server: %d\n", (int)secType); + wmsg(msgbuf, 1); + return rfbSecTypeInvalid; + } - return (int)secType; + return (int)secType; } /* - * Select security type from the server's list (protocol version 3.7) + * Select security type from the server's list (protocol 3.7) */ static int SelectSecurityType(void) { - CARD8 nSecTypes; - char *secTypeNames[] = {"None", "VncAuth"}; - CARD8 knownSecTypes[] = {rfbSecTypeNone, rfbSecTypeVncAuth}; - int nKnownSecTypes = sizeof(knownSecTypes); - CARD8 *secTypes; - CARD8 secType = rfbSecTypeInvalid; - int i, j; - - /* Read the list of secutiry types. */ - if (!ReadFromRFBServer((char *)&nSecTypes, sizeof(nSecTypes))) - return rfbSecTypeInvalid; - - if (nSecTypes == 0) { - ReadConnFailedReason(); - return rfbSecTypeInvalid; - } + CARD8 nSecTypes; + char *secTypeNames[] = {"None", "VncAuth"}; + CARD8 knownSecTypes[] = {rfbSecTypeNone, rfbSecTypeVncAuth}; + int nKnownSecTypes = sizeof(knownSecTypes); + CARD8 *secTypes; + CARD8 secType = rfbSecTypeInvalid; + int i, j; + + if (secTypeNames) {} + + /* Read the list of security types. */ + if (!ReadFromRFBServer((char *)&nSecTypes, sizeof(nSecTypes))) { + return rfbSecTypeInvalid; + } + dt_out = dnow(); - secTypes = malloc(nSecTypes); - if (!ReadFromRFBServer((char *)secTypes, nSecTypes)) - return rfbSecTypeInvalid; - - /* Find out if the server supports TightVNC protocol extensions */ - for (j = 0; j < (int)nSecTypes; j++) { - if (secTypes[j] == rfbSecTypeTight) { - free(secTypes); - secType = rfbSecTypeTight; - if (!WriteExact(rfbsock, (char *)&secType, sizeof(secType))) - return rfbSecTypeInvalid; - fprintf(stderr, "Enabling TightVNC protocol extensions\n"); - return rfbSecTypeTight; - } - } + if (nSecTypes == 0) { + ReadConnFailedReason(); + return rfbSecTypeInvalid; + } - /* Find first supported security type */ - for (j = 0; j < (int)nSecTypes; j++) { - for (i = 0; i < nKnownSecTypes; i++) { - if (secTypes[j] == knownSecTypes[i]) { - secType = secTypes[j]; - if (!WriteExact(rfbsock, (char *)&secType, sizeof(secType))) { - free(secTypes); - return rfbSecTypeInvalid; - } - break; - } - } - if (secType != rfbSecTypeInvalid) break; - } + secTypes = malloc(nSecTypes); + if (!ReadFromRFBServer((char *)secTypes, nSecTypes)) { + return rfbSecTypeInvalid; + } + + if (getenv("SSVNC_DEBUG_SEC_TYPES")) { + for (j = 0; j < (int)nSecTypes; j++) { + fprintf(stderr, "sec-type[%d] %d\n", j, (int) secTypes[j]); + } + } + + /* Find out if the server supports TightVNC protocol extensions */ + for (j = 0; j < (int)nSecTypes; j++) { + if (getenv("VNCVIEWER_NO_SEC_TYPE_TIGHT")) { + break; + } + if (getenv("SSVNC_NO_SEC_TYPE_TIGHT")) { + break; + } +#ifdef TURBOVNC + break; +#endif + if (secTypes[j] == rfbSecTypeTight) { + free(secTypes); + secType = rfbSecTypeTight; + if (!WriteExact(rfbsock, (char *)&secType, sizeof(secType))) { + return rfbSecTypeInvalid; + } + fprintf(stderr, "Enabling TightVNC protocol extensions\n"); + return rfbSecTypeTight; + } + } + + /* Find first supported security type */ + for (j = 0; j < (int)nSecTypes; j++) { + for (i = 0; i < nKnownSecTypes; i++) { + if (secTypes[j] == knownSecTypes[i]) { + secType = secTypes[j]; + if (!WriteExact(rfbsock, (char *)&secType, sizeof(secType))) { + free(secTypes); + return rfbSecTypeInvalid; + } + break; + } + } + if (secType != rfbSecTypeInvalid) { + break; + } + } - free(secTypes); + if (secType == rfbSecTypeInvalid) { + fprintf(stderr, "Server did not offer supported security type:\n"); + for (j = 0; j < (int)nSecTypes; j++) { + fprintf(stderr, " sectype[%d] %d\n", j, (int) secTypes[j]); + } + } - if (secType == rfbSecTypeInvalid) - fprintf(stderr, "Server did not offer supported security type\n"); + free(secTypes); - return (int)secType; + return (int)secType; } @@ -451,6 +1141,9 @@ return True; } +static char *restart_session_pw = NULL; +static int restart_session_len = 0; + /* * Negotiate authentication scheme (protocol version 3.7t) @@ -459,58 +1152,406 @@ static Bool PerformAuthenticationTight(void) { - rfbAuthenticationCapsMsg caps; - CARD32 authScheme; - int i; + rfbAuthenticationCapsMsg caps; + CARD32 authScheme; + int i; - /* In the protocol version 3.7t, the server informs us about supported - authentication schemes. Here we read this information. */ + /* In the protocol version 3.7t, the server informs us about supported + authentication schemes. Here we read this information. */ - if (!ReadFromRFBServer((char *)&caps, sz_rfbAuthenticationCapsMsg)) - return False; + if (!ReadFromRFBServer((char *)&caps, sz_rfbAuthenticationCapsMsg)) { + return False; + } - caps.nAuthTypes = Swap32IfLE(caps.nAuthTypes); + caps.nAuthTypes = Swap32IfLE(caps.nAuthTypes); - if (!caps.nAuthTypes) { - fprintf(stderr, "No authentication needed\n"); - return True; - } + if (!caps.nAuthTypes) { + fprintf(stderr, "No VNC authentication needed\n\n"); + if (viewer_minor >= 8) { + CARD32 authResult; + + if (!ReadFromRFBServer((char *)&authResult, 4)) { + return False; + } + + authResult = Swap32IfLE(authResult); + + if (authResult == rfbVncAuthOK) { + fprintf(stderr, "VNC authentication succeeded (%d) for PerformAuthenticationTight rfbSecTypeNone (RFB 3.8)\n", (int) authResult); + } else { + sprintf(msgbuf, "VNC authentication failed (%d) for PerformAuthenticationTight rfbSecTypeNone (RFB 3.8)\n\n", (int) authResult); + wmsg(msgbuf, 1); + return False; + } + } + return True; + } - if (!ReadCapabilityList(authCaps, caps.nAuthTypes)) - return False; + if (!ReadCapabilityList(authCaps, caps.nAuthTypes)) { + return False; + } - /* Prefer Unix login authentication if a user name was given. */ - if (appData.userLogin && CapsIsEnabled(authCaps, rfbAuthUnixLogin)) { - authScheme = Swap32IfLE(rfbAuthUnixLogin); - if (!WriteExact(rfbsock, (char *)&authScheme, sizeof(authScheme))) - return False; - return AuthenticateUnixLogin(); - } + /* Prefer Unix login authentication if a user name was given. */ + if (appData.userLogin && CapsIsEnabled(authCaps, rfbAuthUnixLogin)) { + authScheme = Swap32IfLE(rfbAuthUnixLogin); + if (!WriteExact(rfbsock, (char *)&authScheme, sizeof(authScheme))) { + return False; + } + return AuthenticateUnixLogin(); + } - /* Otherwise, try server's preferred authentication scheme. */ - for (i = 0; i < CapsNumEnabled(authCaps); i++) { - authScheme = CapsGetByOrder(authCaps, i); - if (authScheme != rfbAuthUnixLogin && authScheme != rfbAuthVNC) - continue; /* unknown scheme - cannot use it */ - authScheme = Swap32IfLE(authScheme); - if (!WriteExact(rfbsock, (char *)&authScheme, sizeof(authScheme))) - return False; - authScheme = Swap32IfLE(authScheme); /* convert it back */ - if (authScheme == rfbAuthUnixLogin) { - return AuthenticateUnixLogin(); - } else if (authScheme == rfbAuthVNC) { - return AuthenticateVNC(); - } else { - /* Should never happen. */ - fprintf(stderr, "Assertion failed: unknown authentication scheme\n"); - return False; - } - } + /* Otherwise, try server's preferred authentication scheme. */ + for (i = 0; i < CapsNumEnabled(authCaps); i++) { + authScheme = CapsGetByOrder(authCaps, i); + if (authScheme != rfbAuthUnixLogin && authScheme != rfbAuthVNC) { + continue; /* unknown scheme - cannot use it */ + } + authScheme = Swap32IfLE(authScheme); + if (!WriteExact(rfbsock, (char *)&authScheme, sizeof(authScheme))) { + return False; + } + authScheme = Swap32IfLE(authScheme); /* convert it back */ + if (authScheme == rfbAuthUnixLogin) { + return AuthenticateUnixLogin(); + } else if (authScheme == rfbAuthVNC) { + return AuthenticateVNC(); + } else { + /* Should never happen. */ + fprintf(stderr, "Assertion failed: unknown authentication scheme\n"); + return False; + } + } - fprintf(stderr, "No suitable authentication schemes offered by server\n"); - return False; + sprintf(msgbuf, "No suitable authentication schemes offered by server\n"); + wmsg(msgbuf, 1); + return False; +} + +#if 0 +unsigned char encPasswd[8]; +unsigned char encPasswd_MSLOGON[32]; +char clearPasswd_MSLOGIN[256]; +static Bool old_ultravnc_mslogon_code(void) { + char *passwd = NULL; + CARD8 challenge_mslogon[CHALLENGESIZE_MSLOGON]; + + /* code from the old uvnc way (1.0.2?) that would go into AuthenticateVNC() template */ + + if (appData.msLogon != NULL) { + raiseme(1); + if (!strcmp(appData.msLogon, "1")) { + char tmp[256]; + fprintf(stderr, "\nUltraVNC MS Logon Username[@Domain]: "); + if (fgets(tmp, 256, stdin) == NULL) { + exit(1); + } + appData.msLogon = strdup(tmp); + } + passwd = getpass("UltraVNC MS Logon Password: "); + if (! passwd) { + exit(1); + } + fprintf(stderr, "\n"); + + UvncEncryptPasswd_MSLOGON(encPasswd_MSLOGON, passwd); + } + if (appData.msLogon) { + if (!ReadFromRFBServer((char *)challenge_mslogon, CHALLENGESIZE_MSLOGON)) { + return False; + } + } + if (appData.msLogon) { + int i; + char tmp[256]; + char *q, *domain = "."; + for (i=0; i < 32; i++) { + challenge_mslogon[i] = encPasswd_MSLOGON[i] ^ challenge_mslogon[i]; + } + q = strchr(appData.msLogon, '@'); + if (q) { + *q = '\0'; + domain = strdup(q+1); + } + memset(tmp, 0, sizeof(tmp)); + strcat(tmp, appData.msLogon); + if (!WriteExact(rfbsock, tmp, 256)) { + return False; + } + memset(tmp, 0, sizeof(tmp)); + strcat(tmp, domain); + if (!WriteExact(rfbsock, tmp, 256)) { + return False; + } + memset(tmp, 0, sizeof(tmp)); + strcat(tmp, passwd); + if (!WriteExact(rfbsock, tmp, CHALLENGESIZE_MSLOGON)) { + return False; + } + } } +#endif +static void hexprint(char *label, char *data, int len) { + int i; + fprintf(stderr, "%s: ", label); + for (i=0; i < len; i++) { + unsigned char c = (unsigned char) data[i]; + fprintf(stderr, "%02x ", (int) c); + if ((i+1) % 20 == 0) { + fprintf(stderr, "\n%s: ", label); + } + } + fprintf(stderr, "\n"); +} + +#define DH_MAX_BITS 31 +static unsigned long long max_dh = ((unsigned long long) 1) << DH_MAX_BITS; + +static unsigned long long bytes_to_uint64(char *bytes) { + unsigned long long result = 0; + int i; + + for (i=0; i < 8; i++) { + result <<= 8; + result += (unsigned char) bytes[i]; + } + return result; +} + +static void uint64_to_bytes(unsigned long long n, char *bytes) { + int i; + + for (i=0; i < 8; i++) { + bytes[i] = (unsigned char) (n >> (8 * (7 - i))); + } +} + +static void try_invert(char *wireuser, char *wirepass, unsigned long long actual_key) { + if (wireuser || wirepass || actual_key) {} + return; +} + + +static unsigned long long XpowYmodN(unsigned long long x, unsigned long long y, unsigned long long N) { + unsigned long long result = 1; + unsigned long long oneShift63 = ((unsigned long long) 1) << 63; + int i; + + for (i = 0; i < 64; y <<= 1, i++) { + result = result * result % N; + if (y & oneShift63) { + result = result * x % N; + } + } + return result; +} + +/* + * UltraVNC MS-Logon authentication (for v1.0.5 and later.) + */ + +/* + * NOTE: The UltraVNC MS-Logon username and password exchange is + * VERY insecure. It can be brute forced in ~2e+9 operations. + * It's not clear we should support it... It is only worth using + * in an environment where no one is sniffing the network, in which + * case all of this DH exchange secrecy is unnecessary... + */ + +static Bool AuthUltraVncMsLogon(void) { + CARD32 authResult; + char gen[8], mod[8], pub[8], rsp[8]; + char user[256], passwd[64], *gpw; + unsigned char key[8]; + unsigned long long ugen, umod, ursp, upub, uprv, ukey; + double now = dnow(); + int db = 0; + + if (getenv("SSVNC_DEBUG_MSLOGON")) { + db = atoi(getenv("SSVNC_DEBUG_MSLOGON")); + } + + fprintf(stderr, "\nAuthUltraVncMsLogon()\n"); + + if (!ReadFromRFBServer(gen, sizeof(gen))) { + return False; + } + if (db) hexprint("gen", gen, sizeof(gen)); + + if (!ReadFromRFBServer(mod, sizeof(mod))) { + return False; + } + if (db) hexprint("mod", mod, sizeof(mod)); + + if (!ReadFromRFBServer(rsp, sizeof(rsp))) { + return False; + } + if (db) hexprint("rsp", rsp, sizeof(rsp)); + + ugen = bytes_to_uint64(gen); + umod = bytes_to_uint64(mod); + ursp = bytes_to_uint64(rsp); + + if (db) { + fprintf(stderr, "ugen: 0x%016llx %12llu\n", ugen, ugen); + fprintf(stderr, "umod: 0x%016llx %12llu\n", umod, umod); + fprintf(stderr, "ursp: 0x%016llx %12llu\n", ursp, ursp); + } + + if (ugen > max_dh) { + fprintf(stderr, "ugen: too big: 0x%016llx\n", ugen); + return False; + } + + if (umod > max_dh) { + fprintf(stderr, "umod: too big: 0x%016llx\n", umod); + return False; + } + + /* make a random long long: */ + uprv = 0xffffffff * (now - (unsigned int) now); + uprv = uprv << 32; + uprv |= (unsigned long long) urandom(); + uprv = uprv % max_dh; + + if (db) fprintf(stderr, "uprv: 0x%016llx %12llu\n", uprv, uprv); + + upub = XpowYmodN(ugen, uprv, umod); + + if (db) fprintf(stderr, "upub: 0x%016llx %12llu\n", upub, upub); + + uint64_to_bytes(upub, pub); + + if (db) hexprint("pub", pub, sizeof(pub)); + + if (!WriteExact(rfbsock, (char *)pub, sizeof(pub))) { + return False; + } + if (db) fprintf(stderr, "wrote pub.\n"); + + if (ursp > max_dh) { + fprintf(stderr, "ursp: too big: 0x%016llx\n", ursp); + return False; + } + + ukey = XpowYmodN(ursp, uprv, umod); + + if (db) fprintf(stderr, "ukey: 0x%016llx %12llu\n", ukey, ukey); + + if (1) { + char tmp[10000]; + tmp[0] = '\0'; + strcat(tmp, "\n"); + strcat(tmp, "WARNING: The UltraVNC Diffie-Hellman Key is weak (key < 2e+9, i.e. 31 bits)\n"); + strcat(tmp, "WARNING: and so an eavesdropper could recover your MS-Logon username and\n"); + strcat(tmp, "WARNING: password via brute force in a few seconds of CPU time. \n"); + strcat(tmp, "WARNING: If this connection is NOT being tunnelled through a separate SSL or\n"); + strcat(tmp, "WARNING: SSH encrypted tunnel, consider things carefully before proceeding...\n"); + strcat(tmp, "WARNING: Do not enter an important username+password when prompted below if\n"); + strcat(tmp, "WARNING: there is a risk of an eavesdropper sniffing this connection.\n"); + strcat(tmp, "WARNING: UltraVNC MSLogon encryption is VERY weak. You've been warned!\n"); + wmsg(tmp, 1); + } + + uint64_to_bytes(ukey, (char *) key); + + if (appData.msLogon == NULL || !strcmp(appData.msLogon, "1")) { + char tmp[256], *q, *s; + if (!use_tty()) { + fprintf(stderr, "\nEnter UltraVNC MS-Logon Username[@Domain] in the popup.\n"); + s = DoUserDialog(); + } else { + raiseme(1); + fprintf(stderr, "\nUltraVNC MS-Logon Username[@Domain]: "); + if (fgets(tmp, 256, stdin) == NULL) { + exit(1); + } + s = strdup(tmp); + } + q = strchr(s, '\n'); + if (q) *q = '\0'; + appData.msLogon = strdup(s); + } + + if (!use_tty()) { + gpw = DoPasswordDialog(); + } else { + raiseme(1); + gpw = getpass("UltraVNC MS-Logon Password: "); + } + if (! gpw) { + return False; + } + fprintf(stderr, "\n"); + + memset(user, 0, sizeof(user)); + strncpy(user, appData.msLogon, 255); + + memset(passwd, 0, sizeof(passwd)); + strncpy(passwd, gpw, 63); + + if (db > 1) { + fprintf(stderr, "user='%s'\n", user); + fprintf(stderr, "pass='%s'\n", passwd); + } + + UvncEncryptBytes2((unsigned char *) user, sizeof(user), key); + UvncEncryptBytes2((unsigned char *) passwd, sizeof(passwd), key); + + if (getenv("TRY_INVERT")) { + try_invert(user, passwd, ukey); + exit(0); + } + + if (db) { + hexprint("user", user, sizeof(user)); + hexprint("pass", passwd, sizeof(passwd)); + } + + if (!WriteExact(rfbsock, user, sizeof(user))) { + return False; + } + if (db) fprintf(stderr, "wrote user.\n"); + + if (!WriteExact(rfbsock, passwd, sizeof(passwd))) { + return False; + } + if (db) fprintf(stderr, "wrote passwd.\n"); + + if (!ReadFromRFBServer((char *) &authResult, 4)) { + return False; + } + authResult = Swap32IfLE(authResult); + + if (db) fprintf(stderr, "authResult: %d\n", (int) authResult); + + switch (authResult) { + case rfbVncAuthOK: + fprintf(stderr, "UVNC MS-Logon authentication succeeded.\n\n"); + break; + case rfbVncAuthFailed: + fprintf(stderr, "UVNC MS-Logon authentication failed.\n"); + if (viewer_minor >= 8) { + printFailureReason(); + } else { + sprintf(msgbuf, "UVNC MS-Logon authentication failed.\n"); + wmsg(msgbuf, 1); + } + fprintf(stderr, "\n"); + return False; + case rfbVncAuthTooMany: + sprintf(msgbuf, "UVNC MS-Logon authentication failed - too many tries.\n\n"); + wmsg(msgbuf, 1); + return False; + default: + sprintf(msgbuf, "Unknown UVNC MS-Logon authentication result: %d\n\n", + (int)authResult); + wmsg(msgbuf, 1); + return False; + } + + return True; +} /* * Standard VNC authentication. @@ -519,80 +1560,119 @@ static Bool AuthenticateVNC(void) { - CARD32 authScheme, authResult; - CARD8 challenge[CHALLENGESIZE]; - char *passwd; - char buffer[64]; - char* cstatus; - int len; + CARD32 authScheme, authResult; + CARD8 challenge[CHALLENGESIZE]; + char *passwd = NULL; + char buffer[64]; + char* cstatus; + int len; + int restart = 0; - fprintf(stderr, "Performing standard VNC authentication\n"); + if (authScheme) {} - if (!ReadFromRFBServer((char *)challenge, CHALLENGESIZE)) - return False; + fprintf(stderr, "\nPerforming standard VNC authentication\n"); - if (appData.passwordFile) { - passwd = vncDecryptPasswdFromFile(appData.passwordFile); - if (!passwd) { - fprintf(stderr, "Cannot read valid password from file \"%s\"\n", - appData.passwordFile); - return False; - } - } else if (appData.autoPass) { - passwd = buffer; - cstatus = fgets(buffer, sizeof buffer, stdin); - if (cstatus == NULL) - buffer[0] = '\0'; - else - { - len = strlen(buffer); - if (len > 0 && buffer[len - 1] == '\n') - buffer[len - 1] = '\0'; - } - } else if (appData.passwordDialog) { - passwd = DoPasswordDialog(); - } else { - passwd = getpass("Password: "); - } + if (!ReadFromRFBServer((char *)challenge, CHALLENGESIZE)) { + return False; + } + + if (restart_session_pw != NULL) { + passwd = restart_session_pw; + restart_session_pw = NULL; + restart = 1; + } else if (appData.passwordFile) { + passwd = vncDecryptPasswdFromFile(appData.passwordFile); + if (!passwd) { + sprintf(msgbuf, "Cannot read valid password from file \"%s\"\n", appData.passwordFile); + wmsg(msgbuf, 1); + return False; + } + } else if (appData.autoPass) { + passwd = buffer; + raiseme(1); + cstatus = fgets(buffer, sizeof buffer, stdin); + if (cstatus == NULL) { + buffer[0] = '\0'; + } else { + len = strlen(buffer); + if (len > 0 && buffer[len - 1] == '\n') { + buffer[len - 1] = '\0'; + } + } + } else if (getenv("VNCVIEWER_PASSWORD")) { + passwd = strdup(getenv("VNCVIEWER_PASSWORD")); + } else if (appData.passwordDialog || !use_tty()) { + passwd = DoPasswordDialog(); + } else { + raiseme(1); + passwd = getpass("VNC Password: "); + } - if (!passwd || strlen(passwd) == 0) { - fprintf(stderr, "Reading password failed\n"); - return False; - } - if (strlen(passwd) > 8) { - passwd[8] = '\0'; - } + if (getenv("VNCVIEWER_PASSWORD")) { + putenv("VNCVIEWER_PASSWORD=none"); + } - vncEncryptBytes(challenge, passwd); + if (restart) { +#define EN0 0 +#define DE1 1 + unsigned char s_fixedkey[8] = {23,82,107,6,35,78,88,7}; + deskey(s_fixedkey, DE1); + des(passwd, passwd); + } else { + if (!passwd || strlen(passwd) == 0) { + sprintf(msgbuf, "Reading password failed\n\n"); + wmsg(msgbuf, 1); + return False; + } + if (strlen(passwd) > 8) { + passwd[8] = '\0'; + } + } - /* Lose the password from memory */ - memset(passwd, '\0', strlen(passwd)); + vncEncryptBytes(challenge, passwd); + - if (!WriteExact(rfbsock, (char *)challenge, CHALLENGESIZE)) - return False; - if (!ReadFromRFBServer((char *)&authResult, 4)) - return False; +#if 0 + /* Lose the password from memory */ + memset(passwd, '\0', strlen(passwd)); +#endif - authResult = Swap32IfLE(authResult); + if (!WriteExact(rfbsock, (char *)challenge, CHALLENGESIZE)) { + return False; + } - switch (authResult) { - case rfbVncAuthOK: - fprintf(stderr, "VNC authentication succeeded\n"); - break; - case rfbVncAuthFailed: - fprintf(stderr, "VNC authentication failed\n"); - return False; - case rfbVncAuthTooMany: - fprintf(stderr, "VNC authentication failed - too many tries\n"); - return False; - default: - fprintf(stderr, "Unknown VNC authentication result: %d\n", - (int)authResult); - return False; - } + if (!ReadFromRFBServer((char *)&authResult, 4)) { + return False; + } - return True; + authResult = Swap32IfLE(authResult); + + switch (authResult) { + case rfbVncAuthOK: + fprintf(stderr, "VNC authentication succeeded\n\n"); + break; + case rfbVncAuthFailed: + fprintf(stderr, "VNC authentication failed.\n"); + if (viewer_minor >= 8) { + printFailureReason(); + } else { + sprintf(msgbuf, "VNC authentication failed.\n"); + wmsg(msgbuf, 1); + } + fprintf(stderr, "\n"); + return False; + case rfbVncAuthTooMany: + sprintf(msgbuf, "VNC authentication failed - too many tries\n\n"); + wmsg(msgbuf, 1); + return False; + default: + sprintf(msgbuf, "Unknown VNC authentication result: %d\n\n", (int)authResult); + wmsg(msgbuf, 1); + return False; + } + + return True; } /* @@ -602,68 +1682,77 @@ static Bool AuthenticateUnixLogin(void) { - CARD32 loginLen, passwdLen, authResult; - char *login; - char *passwd; - struct passwd *ps; - - fprintf(stderr, "Performing Unix login-style authentication\n"); - - if (appData.userLogin) { - login = appData.userLogin; - } else { - ps = getpwuid(getuid()); - login = ps->pw_name; - } + CARD32 loginLen, passwdLen, authResult; + char *login; + char *passwd; + struct passwd *ps; + + fprintf(stderr, "\nPerforming Unix login-style authentication\n"); + + if (appData.userLogin) { + login = appData.userLogin; + } else { + ps = getpwuid(getuid()); + login = ps->pw_name; + } - fprintf(stderr, "Using user name \"%s\"\n", login); + fprintf(stderr, "Using user name \"%s\"\n", login); - if (appData.passwordDialog) { - passwd = DoPasswordDialog(); - } else { - passwd = getpass("Password: "); - } - if (!passwd || strlen(passwd) == 0) { - fprintf(stderr, "Reading password failed\n"); - return False; - } + if (appData.passwordDialog || !use_tty()) { + passwd = DoPasswordDialog(); + } else { + raiseme(1); + passwd = getpass("VNC Password: "); + } + if (!passwd || strlen(passwd) == 0) { + fprintf(stderr, "Reading password failed\n"); + return False; + } - loginLen = Swap32IfLE((CARD32)strlen(login)); - passwdLen = Swap32IfLE((CARD32)strlen(passwd)); + loginLen = Swap32IfLE((CARD32)strlen(login)); + passwdLen = Swap32IfLE((CARD32)strlen(passwd)); - if (!WriteExact(rfbsock, (char *)&loginLen, sizeof(loginLen)) || - !WriteExact(rfbsock, (char *)&passwdLen, sizeof(passwdLen))) - return False; + if (!WriteExact(rfbsock, (char *)&loginLen, sizeof(loginLen)) || + !WriteExact(rfbsock, (char *)&passwdLen, sizeof(passwdLen))) { + return False; + } - if (!WriteExact(rfbsock, login, strlen(login)) || - !WriteExact(rfbsock, passwd, strlen(passwd))) - return False; + if (!WriteExact(rfbsock, login, strlen(login)) || + !WriteExact(rfbsock, passwd, strlen(passwd))) { + return False; + } - /* Lose the password from memory */ - memset(passwd, '\0', strlen(passwd)); +#if 0 + /* Lose the password from memory */ + memset(passwd, '\0', strlen(passwd)); +#endif - if (!ReadFromRFBServer((char *)&authResult, sizeof(authResult))) - return False; + if (!ReadFromRFBServer((char *)&authResult, sizeof(authResult))) { + return False; + } - authResult = Swap32IfLE(authResult); + authResult = Swap32IfLE(authResult); - switch (authResult) { - case rfbVncAuthOK: - fprintf(stderr, "Authentication succeeded\n"); - break; - case rfbVncAuthFailed: - fprintf(stderr, "Authentication failed\n"); - return False; - case rfbVncAuthTooMany: - fprintf(stderr, "Authentication failed - too many tries\n"); - return False; - default: - fprintf(stderr, "Unknown authentication result: %d\n", - (int)authResult); - return False; - } + switch (authResult) { + case rfbVncAuthOK: + fprintf(stderr, "Authentication succeeded\n\n"); + break; + case rfbVncAuthFailed: + sprintf(msgbuf, "Authentication failed\n\n"); + wmsg(msgbuf, 1); + return False; + case rfbVncAuthTooMany: + sprintf(msgbuf, "Authentication failed - too many tries\n\n"); + wmsg(msgbuf, 1); + return False; + default: + sprintf(msgbuf, "Unknown authentication result: %d\n\n", + (int)authResult); + wmsg(msgbuf, 1); + return False; + } - return True; + return True; } @@ -675,19 +1764,20 @@ static Bool ReadInteractionCaps(void) { - rfbInteractionCapsMsg intr_caps; + rfbInteractionCapsMsg intr_caps; - /* Read the counts of list items following */ - if (!ReadFromRFBServer((char *)&intr_caps, sz_rfbInteractionCapsMsg)) - return False; - intr_caps.nServerMessageTypes = Swap16IfLE(intr_caps.nServerMessageTypes); - intr_caps.nClientMessageTypes = Swap16IfLE(intr_caps.nClientMessageTypes); - intr_caps.nEncodingTypes = Swap16IfLE(intr_caps.nEncodingTypes); - - /* Read the lists of server- and client-initiated messages */ - return (ReadCapabilityList(serverMsgCaps, intr_caps.nServerMessageTypes) && - ReadCapabilityList(clientMsgCaps, intr_caps.nClientMessageTypes) && - ReadCapabilityList(encodingCaps, intr_caps.nEncodingTypes)); + /* Read the counts of list items following */ + if (!ReadFromRFBServer((char *)&intr_caps, sz_rfbInteractionCapsMsg)) { + return False; + } + intr_caps.nServerMessageTypes = Swap16IfLE(intr_caps.nServerMessageTypes); + intr_caps.nClientMessageTypes = Swap16IfLE(intr_caps.nClientMessageTypes); + intr_caps.nEncodingTypes = Swap16IfLE(intr_caps.nEncodingTypes); + + /* Read the lists of server- and client-initiated messages */ + return (ReadCapabilityList(serverMsgCaps, intr_caps.nServerMessageTypes) && + ReadCapabilityList(clientMsgCaps, intr_caps.nClientMessageTypes) && + ReadCapabilityList(encodingCaps, intr_caps.nEncodingTypes)); } @@ -697,22 +1787,70 @@ * many records to read from the socket. */ -static Bool -ReadCapabilityList(CapsContainer *caps, int count) -{ - rfbCapabilityInfo msginfo; - int i; +static Bool +ReadCapabilityList(CapsContainer *caps, int count) +{ + rfbCapabilityInfo msginfo; + int i; + + for (i = 0; i < count; i++) { + if (!ReadFromRFBServer((char *)&msginfo, sz_rfbCapabilityInfo)) { + return False; + } + msginfo.code = Swap32IfLE(msginfo.code); + CapsEnable(caps, &msginfo); + } + + return True; +} + - for (i = 0; i < count; i++) { - if (!ReadFromRFBServer((char *)&msginfo, sz_rfbCapabilityInfo)) - return False; - msginfo.code = Swap32IfLE(msginfo.code); - CapsEnable(caps, &msginfo); - } +/* used to have !tunnelSpecified */ - return True; +static int guess_compresslevel(void) { + int n; + if (latency > 200.0) { + n = 8; + } else if (latency > 100.0) { + n = 7; + } else if (latency > 60.0) { + n = 6; + } else if (latency > 15.0) { + n = 4; + } else if (latency > 8.0) { + n = 2; + } else if (latency > 0.0) { + n = 1; + } else { + /* no latency measurement */ + n = 3; + } + return n; } +static int guess_qualitylevel(void) { + int n; + if (latency > 200.0) { + n = 4; + } else if (latency > 100.0) { + n = 5; + } else if (latency > 60.0) { + n = 6; + } else if (latency > 15.0) { + n = 7; + } else if (latency > 8.0) { + n = 8; + } else if (latency > 0.0) { + n = 9; + } else { + /* no latency measurement */ + n = 6; + } +#ifdef TURBOVNC + n *= 10; +#endif + return n; +} /* * SetFormatAndEncodings. @@ -729,6 +1867,21 @@ Bool requestCompressLevel = False; Bool requestQualityLevel = False; Bool requestLastRectEncoding = False; + Bool requestNewFBSizeEncoding = True; + Bool requestTextChatEncoding = True; + Bool requestSubsampLevel = False; + int dsm = 0; + int tQL, tQLmax = 9; + static int qlmsg = 0, clmsg = 0; +#ifdef TURBOVNC + tQLmax = 100; +#endif + + if (requestTextChatEncoding || requestSubsampLevel || tQL) {} + +#if 0 + fprintf(stderr, "SetFormatAndEncodings: sent_FBU state: %2d\n", sent_FBU); +#endif spf.type = rfbSetPixelFormat; spf.format = myFormat; @@ -736,15 +1889,32 @@ spf.format.greenMax = Swap16IfLE(spf.format.greenMax); spf.format.blueMax = Swap16IfLE(spf.format.blueMax); + + currentMsg = rfbSetPixelFormat; if (!WriteExact(rfbsock, (char *)&spf, sz_rfbSetPixelFormatMsg)) return False; se->type = rfbSetEncodings; se->nEncodings = 0; + if (appData.ultraDSM) { + dsm = 1; + } + if (appData.encodingsString) { char *encStr = appData.encodingsString; int encStrLen; + if (strchr(encStr, ',')) { + char *p; + encStr = strdup(encStr); + p = encStr; + while (*p != '\0') { + if (*p == ',') { + *p = ' '; + } + p++; + } + } do { char *nextEncStr = strchr(encStr, ' '); if (nextEncStr) { @@ -754,50 +1924,102 @@ encStrLen = strlen(encStr); } +if (getenv("DEBUG_SETFORMAT")) { + fprintf(stderr, "encs: "); + write(2, encStr, encStrLen); + fprintf(stderr, "\n"); +} + if (strncasecmp(encStr,"raw",encStrLen) == 0) { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRaw); } else if (strncasecmp(encStr,"copyrect",encStrLen) == 0) { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCopyRect); - } else if (strncasecmp(encStr,"tight",encStrLen) == 0) { + } else if (strncasecmp(encStr,"tight",encStrLen) == 0 && !dsm) { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingTight); requestLastRectEncoding = True; - if (appData.compressLevel >= 0 && appData.compressLevel <= 9) - requestCompressLevel = True; - if (appData.enableJPEG) - requestQualityLevel = True; + if (appData.compressLevel >= 0 && appData.compressLevel <= 9) { + requestCompressLevel = True; + } + if (appData.enableJPEG) { + requestQualityLevel = True; + } +#ifdef TURBOVNC + requestSubsampLevel = True; +#endif } else if (strncasecmp(encStr,"hextile",encStrLen) == 0) { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile); - } else if (strncasecmp(encStr,"zlib",encStrLen) == 0) { + } else if (strncasecmp(encStr,"zlib",encStrLen) == 0 && !dsm) { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZlib); - if (appData.compressLevel >= 0 && appData.compressLevel <= 9) - requestCompressLevel = True; - } else if (strncasecmp(encStr,"corre",encStrLen) == 0) { + if (appData.compressLevel >= 0 && appData.compressLevel <= 9) { + requestCompressLevel = True; + } + } else if (strncasecmp(encStr,"corre",encStrLen) == 0 && !dsm) { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCoRRE); } else if (strncasecmp(encStr,"rre",encStrLen) == 0) { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRRE); + } else if (strncasecmp(encStr,"zrle",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZRLE); +#if DO_ZYWRLE + } else if (strncasecmp(encStr,"zywrle",encStrLen) == 0) { + int qlevel = appData.qualityLevel; + if (qlevel < 0 || qlevel > tQLmax) qlevel = guess_qualitylevel(); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZYWRLE); + requestQualityLevel = True; + if (qlevel < 3) { + zywrle_level = 3; + } else if (qlevel < 6) { + zywrle_level = 2; + } else { + zywrle_level = 1; + } +#endif } else { fprintf(stderr,"Unknown encoding '%.*s'\n",encStrLen,encStr); + if (dsm && strstr(encStr, "tight") == encStr) fprintf(stderr, "tight encoding does not yet work with ultraDSM, skipping it.\n"); + if (dsm && strstr(encStr, "corre") == encStr) fprintf(stderr, "corre encoding does not yet work with ultraDSM, skipping it.\n"); + if (dsm && strstr(encStr, "zlib" ) == encStr) fprintf(stderr, "zlib encoding does not yet work with ultraDSM, skipping it.\n"); } encStr = nextEncStr; } while (encStr && se->nEncodings < MAX_ENCODINGS); if (se->nEncodings < MAX_ENCODINGS && requestCompressLevel) { - encs[se->nEncodings++] = Swap32IfLE(appData.compressLevel + - rfbEncodingCompressLevel0); + ; + } else if (se->nEncodings < MAX_ENCODINGS) { + appData.compressLevel = guess_compresslevel(); + if (clmsg++ == 0) fprintf(stderr, "guessed: -compresslevel %d\n", appData.compressLevel); } + encs[se->nEncodings++] = Swap32IfLE(appData.compressLevel + rfbEncodingCompressLevel0); if (se->nEncodings < MAX_ENCODINGS && requestQualityLevel) { - if (appData.qualityLevel < 0 || appData.qualityLevel > 9) - appData.qualityLevel = 5; - encs[se->nEncodings++] = Swap32IfLE(appData.qualityLevel + - rfbEncodingQualityLevel0); + if (appData.qualityLevel < 0 || appData.qualityLevel > tQLmax) { + appData.qualityLevel = guess_qualitylevel(); + if (qlmsg++ == 0) fprintf(stderr, "guessed: -qualitylevel %d\n", appData.qualityLevel); + } + } else if (se->nEncodings < MAX_ENCODINGS) { + appData.qualityLevel = guess_qualitylevel(); + if (qlmsg++ == 0) fprintf(stderr, "guessed: -qualitylevel %d\n", appData.qualityLevel); + } +#ifdef TURBOVNC + tQL = appData.qualityLevel / 10; + if (tQL < 0) tQL = 1; + if (tQL > 9) tQL = 9; + encs[se->nEncodings++] = Swap32IfLE(tQL + rfbEncodingQualityLevel0); + encs[se->nEncodings++] = Swap32IfLE(appData.qualityLevel + rfbJpegQualityLevel1 - 1); + if (se->nEncodings < MAX_ENCODINGS && requestSubsampLevel) { + if (appData.subsampLevel < 0 || appData.subsampLevel > TVNC_SAMPOPT - 1) { + appData.subsampLevel = TVNC_1X; + } + encs[se->nEncodings++] = Swap32IfLE(appData.subsampLevel + rfbJpegSubsamp1X); } +#else + encs[se->nEncodings++] = Swap32IfLE(appData.qualityLevel + rfbEncodingQualityLevel0); +#endif if (appData.useRemoteCursor) { if (se->nEncodings < MAX_ENCODINGS) encs[se->nEncodings++] = Swap32IfLE(rfbEncodingXCursor); - if (se->nEncodings < MAX_ENCODINGS) + if (se->nEncodings < MAX_ENCODINGS && !appData.useX11Cursor) encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRichCursor); if (se->nEncodings < MAX_ENCODINGS) encs[se->nEncodings++] = Swap32IfLE(rfbEncodingPointerPos); @@ -806,10 +2028,16 @@ if (se->nEncodings < MAX_ENCODINGS && requestLastRectEncoding) { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingLastRect); } - } - else { + + if (se->nEncodings < MAX_ENCODINGS && requestNewFBSizeEncoding) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingNewFBSize); + } + + } else { + /* DIFFERENT CASE */ + if (SameMachine(rfbsock)) { - if (!tunnelSpecified) { + if (!tunnelSpecified && appData.useRawLocal) { fprintf(stderr,"Same machine: preferring raw encoding\n"); encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRaw); } else { @@ -818,44 +2046,84 @@ } encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCopyRect); - encs[se->nEncodings++] = Swap32IfLE(rfbEncodingTight); + if (!dsm) encs[se->nEncodings++] = Swap32IfLE(rfbEncodingTight); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZRLE); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZYWRLE); encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile); - encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZlib); - encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCoRRE); + if (!dsm) encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZlib); + if (!dsm) encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCoRRE); encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRRE); - if (appData.compressLevel >= 0 && appData.compressLevel <= 9) { - encs[se->nEncodings++] = Swap32IfLE(appData.compressLevel + - rfbEncodingCompressLevel0); - } else if (!tunnelSpecified) { - /* If -tunnel option was provided, we assume that server machine is - not in the local network so we use default compression level for - tight encoding instead of fast compression. Thus we are - requesting level 1 compression only if tunneling is not used. */ - encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCompressLevel1); - } - - if (appData.enableJPEG) { - if (appData.qualityLevel < 0 || appData.qualityLevel > 9) - appData.qualityLevel = 5; - encs[se->nEncodings++] = Swap32IfLE(appData.qualityLevel + - rfbEncodingQualityLevel0); + if (!dsm && appData.compressLevel >= 0 && appData.compressLevel <= 9) { + encs[se->nEncodings++] = Swap32IfLE(appData.compressLevel + rfbEncodingCompressLevel0); + } else { + /* + * OUT OF DATE: If -tunnel option was provided, we assume that server machine is + * not in the local network so we use default compression level for + * tight encoding instead of fast compression. Thus we are + * requesting level 1 compression only if tunneling is not used. + */ + appData.compressLevel = guess_compresslevel(); + if (clmsg++ == 0) fprintf(stderr, "guessed: -compresslevel %d\n", appData.compressLevel); + encs[se->nEncodings++] = Swap32IfLE(appData.compressLevel + rfbEncodingCompressLevel0); + } + + if (!dsm && appData.enableJPEG) { + if (appData.qualityLevel < 0 || appData.qualityLevel > tQLmax) { + appData.qualityLevel = guess_qualitylevel(); + if (qlmsg++ == 0) fprintf(stderr, "guessed: -qualitylevel %d\n", appData.qualityLevel); + } + +#ifdef TURBOVNC + requestSubsampLevel = True; + tQL = appData.qualityLevel / 10; + if (tQL < 0) tQL = 1; + if (tQL > 9) tQL = 9; + encs[se->nEncodings++] = Swap32IfLE(tQL + rfbEncodingQualityLevel0); + encs[se->nEncodings++] = Swap32IfLE(appData.qualityLevel + rfbJpegQualityLevel1 - 1); + if (se->nEncodings < MAX_ENCODINGS && requestSubsampLevel) { + if (appData.subsampLevel < 0 || appData.subsampLevel > TVNC_SAMPOPT - 1) { + appData.subsampLevel = TVNC_1X; + } + encs[se->nEncodings++] = Swap32IfLE(appData.subsampLevel + rfbJpegSubsamp1X); + } +#else + encs[se->nEncodings++] = Swap32IfLE(appData.qualityLevel + rfbEncodingQualityLevel0); +#endif + } if (appData.useRemoteCursor) { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingXCursor); - encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRichCursor); + if (!appData.useX11Cursor) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRichCursor); + } encs[se->nEncodings++] = Swap32IfLE(rfbEncodingPointerPos); } encs[se->nEncodings++] = Swap32IfLE(rfbEncodingLastRect); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingNewFBSize); } len = sz_rfbSetEncodingsMsg + se->nEncodings * 4; - se->nEncodings = Swap16IfLE(se->nEncodings); + if (!appData.ultraDSM) { + se->nEncodings = Swap16IfLE(se->nEncodings); - if (!WriteExact(rfbsock, buf, len)) return False; + if (!WriteExact(rfbsock, buf, len)) return False; + } else { + /* for UltraVNC encryption DSM we have to send each encoding separately (why?) */ + int i, errs = 0, nenc = se->nEncodings; + + se->nEncodings = Swap16IfLE(se->nEncodings); + + currentMsg = rfbSetEncodings; + if (!WriteExact(rfbsock, buf, sz_rfbSetEncodingsMsg)) errs++; + for (i=0; i < nenc; i++) { + if (!WriteExact(rfbsock, (char *)&encs[i], sizeof(CARD32))) errs++; + } + if (errs) return False; + } return True; } @@ -868,31 +2136,86 @@ Bool SendIncrementalFramebufferUpdateRequest() { - return SendFramebufferUpdateRequest(0, 0, si.framebufferWidth, - si.framebufferHeight, True); + return SendFramebufferUpdateRequest(0, 0, si.framebufferWidth, + si.framebufferHeight, True); } +time_t last_filexfer = 0; +int delay_filexfer = 3; +extern void CheckFileXfer(void); +extern int rfbsock_is_ready(void); + + +static int dyn = -1; +extern int filexfer_sock; +extern int filexfer_listen; /* * SendFramebufferUpdateRequest. */ - Bool SendFramebufferUpdateRequest(int x, int y, int w, int h, Bool incremental) { - rfbFramebufferUpdateRequestMsg fur; + rfbFramebufferUpdateRequestMsg fur; + static int db = -1; - fur.type = rfbFramebufferUpdateRequest; - fur.incremental = incremental ? 1 : 0; - fur.x = Swap16IfLE(x); - fur.y = Swap16IfLE(y); - fur.w = Swap16IfLE(w); - fur.h = Swap16IfLE(h); + if (db < 0) { + if (getenv("SSVNC_DEBUG_RECTS")) { + db = atoi(getenv("SSVNC_DEBUG_RECTS")); + } else { + db = 0; + } + } - if (!WriteExact(rfbsock, (char *)&fur, sz_rfbFramebufferUpdateRequestMsg)) - return False; + if (db) fprintf(stderr, "SendFramebufferUpdateRequest(%d, %d, %d, %d, incremental=%d)\n", x, y, w, h, (int) incremental); - return True; + if (dyn < 0) { + struct stat sb; + if (getenv("USER") && !strcmp(getenv("USER"), "runge")) { + if (stat("/tmp/nodyn", &sb) == 0) { + putenv("NOFTFBUPDATES=1"); + unlink("/tmp/nodyn"); + } + } + if (getenv("NOFTFBUPDATES")) { + dyn = 0; + } else { + dyn = 1; + } + } + + if (appData.fileActive && filexfer_sock >= 0) { + static int first = 1; + if (first) { + fprintf(stderr, "SFU: dynamic fb updates during filexfer: %d\n", dyn); + first = 0; + } +if (db > 2 || 0) fprintf(stderr, "A sfur: %d %d %d %d d_last: %d\n", x, y, w, h, (int) (time(NULL) - last_filexfer)); + if (!dyn || time(NULL) < last_filexfer + delay_filexfer) { + return True; + } + } +if (db > 1) fprintf(stderr, "B sfur: %d %d %d %d\n", x, y, w, h); + + fur.type = rfbFramebufferUpdateRequest; + fur.incremental = incremental ? 1 : 0; + fur.x = Swap16IfLE(x); + fur.y = Swap16IfLE(y); + fur.w = Swap16IfLE(w); + fur.h = Swap16IfLE(h); + + if (incremental) { + sent_FBU = 1; + } else { + sent_FBU = 2; + } + + currentMsg = rfbFramebufferUpdateRequest; + if (!WriteExact(rfbsock, (char *)&fur, sz_rfbFramebufferUpdateRequestMsg)) { + return False; + } + + return True; } @@ -903,19 +2226,38 @@ Bool SendPointerEvent(int x, int y, int buttonMask) { - rfbPointerEventMsg pe; + rfbPointerEventMsg pe; + + if (appData.fileActive) { + if (!dyn || time(NULL) < last_filexfer + delay_filexfer) { +#if 0 + fprintf(stderr, "skip SendPointerEvent: %d - %d\n", last_filexfer, time(NULL)); +#endif + return True; + } + } - pe.type = rfbPointerEvent; - pe.buttonMask = buttonMask; - if (x < 0) x = 0; - if (y < 0) y = 0; - - if (!appData.useX11Cursor) - SoftCursorMove(x, y); - - pe.x = Swap16IfLE(x); - pe.y = Swap16IfLE(y); - return WriteExact(rfbsock, (char *)&pe, sz_rfbPointerEventMsg); + pe.type = rfbPointerEvent; + pe.buttonMask = buttonMask; + + if (scale_factor_x > 0.0 && scale_factor_x != 1.0) { + x /= scale_factor_x; + } + if (scale_factor_y > 0.0 && scale_factor_y != 1.0) { + y /= scale_factor_y; + } + + if (x < 0) x = 0; + if (y < 0) y = 0; + + if (!appData.useX11Cursor) { + SoftCursorMove(x, y); + } + + pe.x = Swap16IfLE(x); + pe.y = Swap16IfLE(y); + currentMsg = rfbPointerEvent; + return WriteExact(rfbsock, (char *)&pe, sz_rfbPointerEventMsg); } @@ -926,12 +2268,22 @@ Bool SendKeyEvent(CARD32 key, Bool down) { - rfbKeyEventMsg ke; + rfbKeyEventMsg ke; + + if (appData.fileActive) { + if (!dyn || time(NULL) < last_filexfer + delay_filexfer) { +#if 0 + fprintf(stderr, "skip SendPointerEvent: %d - %d\n", last_filexfer, time(NULL)); +#endif + return True; + } + } - ke.type = rfbKeyEvent; - ke.down = down ? 1 : 0; - ke.key = Swap32IfLE(key); - return WriteExact(rfbsock, (char *)&ke, sz_rfbKeyEventMsg); + ke.type = rfbKeyEvent; + ke.down = down ? 1 : 0; + ke.key = Swap32IfLE(key); + currentMsg = rfbKeyEvent; + return WriteExact(rfbsock, (char *)&ke, sz_rfbKeyEventMsg); } @@ -942,281 +2294,1025 @@ Bool SendClientCutText(char *str, int len) { - rfbClientCutTextMsg cct; + rfbClientCutTextMsg cct; + + if (serverCutText) { + free(serverCutText); + } + serverCutText = NULL; + + if (appData.fileActive) { + if (!dyn || time(NULL) < last_filexfer + delay_filexfer) { + /* ultravnc java viewer lets this one through. */ + return True; + } + } + + if (appData.viewOnly) { + return True; + } - if (serverCutText) - free(serverCutText); - serverCutText = NULL; - - cct.type = rfbClientCutText; - cct.length = Swap32IfLE(len); - return (WriteExact(rfbsock, (char *)&cct, sz_rfbClientCutTextMsg) && - WriteExact(rfbsock, str, len)); + cct.type = rfbClientCutText; + cct.length = Swap32IfLE((unsigned int) len); + currentMsg = rfbClientCutText; + return (WriteExact(rfbsock, (char *)&cct, sz_rfbClientCutTextMsg) && + WriteExact(rfbsock, str, len)); } +static int ultra_scale = 0; -/* - * HandleRFBServerMessage. - */ +Bool +SendServerScale(int nfac) +{ + rfbSetScaleMsg ssc; + if (nfac < 0 || nfac > 100) { + return True; + } + + ultra_scale = nfac; + ssc.type = rfbSetScale; + ssc.scale = nfac; + currentMsg = rfbSetScale; + return WriteExact(rfbsock, (char *)&ssc, sz_rfbSetScaleMsg); +} Bool -HandleRFBServerMessage() +SendServerInput(Bool enabled) { - rfbServerToClientMsg msg; + rfbSetServerInputMsg sim; - if (!ReadFromRFBServer((char *)&msg, 1)) - return False; + sim.type = rfbSetServerInput; + sim.status = enabled; + currentMsg = rfbSetServerInput; + return WriteExact(rfbsock, (char *)&sim, sz_rfbSetServerInputMsg); +} - switch (msg.type) { +Bool +SendSingleWindow(int x, int y) +{ + static int w_old = -1, h_old = -1; + rfbSetSWMsg sw; - case rfbSetColourMapEntries: - { - int i; - CARD16 rgb[3]; - XColor xc; + fprintf(stderr, "SendSingleWindow: %d %d\n", x, y); - if (!ReadFromRFBServer(((char *)&msg) + 1, - sz_rfbSetColourMapEntriesMsg - 1)) - return False; + if (x == -1 && y == -1) { + sw.type = rfbSetSW; + sw.x = Swap16IfLE(1); + sw.y = Swap16IfLE(1); + if (w_old > 0) { + si.framebufferWidth = w_old; + si.framebufferHeight = h_old; + ReDoDesktop(); + } + w_old = h_old = -1; + } else { + sw.type = rfbSetSW; + sw.x = Swap16IfLE(x); + sw.y = Swap16IfLE(y); + w_old = si.framebufferWidth; + h_old = si.framebufferHeight; + + } + sw.status = True; + currentMsg = rfbSetSW; + return WriteExact(rfbsock, (char *)&sw, sz_rfbSetSWMsg); +} - msg.scme.firstColour = Swap16IfLE(msg.scme.firstColour); - msg.scme.nColours = Swap16IfLE(msg.scme.nColours); +Bool +SendTextChat(char *str) +{ + static int db = -1; + rfbTextChatMsg chat; - for (i = 0; i < msg.scme.nColours; i++) { - if (!ReadFromRFBServer((char *)rgb, 6)) - return False; - xc.pixel = msg.scme.firstColour + i; - xc.red = Swap16IfLE(rgb[0]); - xc.green = Swap16IfLE(rgb[1]); - xc.blue = Swap16IfLE(rgb[2]); - xc.flags = DoRed|DoGreen|DoBlue; - XStoreColor(dpy, cmap, &xc); - } + if (db < 0) { + if (getenv("SSVNC_DEBUG_CHAT")) { + db = 1; + } else { + db = 0; + } + } + if (!appData.chatActive) { + SendTextChatOpen(); + appData.chatActive = True; + } - break; - } + chat.type = rfbTextChat; + chat.pad1 = 0; + chat.pad2 = 0; + chat.length = (unsigned int) strlen(str); + if (db) fprintf(stderr, "SendTextChat: %d '%s'\n", (int) chat.length, str); + chat.length = Swap32IfLE(chat.length); + if (!WriteExact(rfbsock, (char *)&chat, sz_rfbTextChatMsg)) { + return False; + } + currentMsg = rfbTextChat; + return WriteExact(rfbsock, str, strlen(str)); +} - case rfbFramebufferUpdate: - { - rfbFramebufferUpdateRectHeader rect; - int linesToRead; - int bytesPerLine; - int i; - int usecs; +extern void raiseme(int force); - if (!ReadFromRFBServer(((char *)&msg.fu) + 1, - sz_rfbFramebufferUpdateMsg - 1)) - return False; +Bool +SendTextChatOpen(void) +{ + rfbTextChatMsg chat; - msg.fu.nRects = Swap16IfLE(msg.fu.nRects); + raiseme(0); + chat.type = rfbTextChat; + chat.pad1 = 0; + chat.pad2 = 0; + chat.length = Swap32IfLE(rfbTextChatOpen); + return WriteExact(rfbsock, (char *)&chat, sz_rfbTextChatMsg); +} - for (i = 0; i < msg.fu.nRects; i++) { - if (!ReadFromRFBServer((char *)&rect, sz_rfbFramebufferUpdateRectHeader)) - return False; +Bool +SendTextChatClose(void) +{ + rfbTextChatMsg chat; + chat.type = rfbTextChat; + chat.pad1 = 0; + chat.pad2 = 0; + chat.length = Swap32IfLE(rfbTextChatClose); + appData.chatActive = False; + return WriteExact(rfbsock, (char *)&chat, sz_rfbTextChatMsg); +} - rect.encoding = Swap32IfLE(rect.encoding); - if (rect.encoding == rfbEncodingLastRect) - break; +Bool +SendTextChatFinished(void) +{ + rfbTextChatMsg chat; + chat.type = rfbTextChat; + chat.pad1 = 0; + chat.pad2 = 0; + chat.length = Swap32IfLE(rfbTextChatFinished); + appData.chatActive = False; + return WriteExact(rfbsock, (char *)&chat, sz_rfbTextChatMsg); +} + +extern int do_format_change; +extern int do_cursor_change; +extern double do_fb_update; +extern void cutover_format_change(void); + +double dtime(double *t_old) { + /* + * usage: call with 0.0 to initialize, subsequent calls give + * the time difference since last call. + */ + double t_now, dt; + struct timeval now; + + gettimeofday(&now, NULL); + t_now = now.tv_sec + ( (double) now.tv_usec/1000000. ); + if (*t_old == 0.0) { + *t_old = t_now; + return t_now; + } + dt = t_now - *t_old; + *t_old = t_now; + return(dt); +} + +/* common dtime() activities: */ +double dtime0(double *t_old) { + *t_old = 0.0; + return dtime(t_old); +} + +double dnow(void) { + double t; + return dtime0(&t); +} + +static char fxfer[65536]; + +Bool HandleFileXfer(void) { + unsigned char hdr[12]; + unsigned int len; + + int rfbDirContentRequest = 1; + int rfbDirPacket = 2; /* Full directory name or full file name. */ + int rfbFileTransferRequest = 3; + int rfbFileHeader = 4; + int rfbFilePacket = 5; /* One slice of the file */ + int rfbEndOfFile = 6; + int rfbAbortFileTransfer = 7; + int rfbFileTransferOffer = 8; + int rfbFileAcceptHeader = 9; /* The server accepts or rejects the file */ + int rfbCommand = 10; + int rfbCommandReturn = 11; + int rfbFileChecksums = 12; + + int rfbRDirContent = 1; /* Request a Server Directory contents */ + int rfbRDrivesList = 2; /* Request the server's drives list */ + + int rfbADirectory = 1; /* Reception of a directory name */ + int rfbAFile = 2; /* Reception of a file name */ + int rfbADrivesList = 3; /* Reception of a list of drives */ + int rfbADirCreate = 4; /* Response to a create dir command */ + int rfbADirDelete = 5; /* Response to a delete dir command */ + int rfbAFileCreate = 6; /* Response to a create file command */ + int rfbAFileDelete = 7; /* Response to a delete file command */ + + int rfbCDirCreate = 1; /* Request the server to create the given directory */ + int rfbCDirDelete = 2; /* Request the server to delete the given directory */ + int rfbCFileCreate = 3; /* Request the server to create the given file */ + int rfbCFileDelete = 4; /* Request the server to delete the given file */ + + int rfbRErrorUnknownCmd = 1; /* Unknown FileTransfer command. */ +#define rfbRErrorCmd 0xFFFFFFFF + + static int db = -1; + static int guess_x11vnc = 0; + +#if 0 + if (filexfer_sock < 0) { + return True; + } + /* instead, we read and discard the ft msg data. */ +#endif + +/*fprintf(stderr, "In HandleFileXfer\n"); */ - rect.r.x = Swap16IfLE(rect.r.x); - rect.r.y = Swap16IfLE(rect.r.y); - rect.r.w = Swap16IfLE(rect.r.w); - rect.r.h = Swap16IfLE(rect.r.h); - - if (rect.encoding == rfbEncodingXCursor || - rect.encoding == rfbEncodingRichCursor) { - if (!HandleCursorShape(rect.r.x, rect.r.y, rect.r.w, rect.r.h, - rect.encoding)) { - return False; + if (db < 0) { + if (getenv("DEBUG_HandleFileXfer")) { + db = 1; + } else { + db = 0; + } } - continue; - } - if (rect.encoding == rfbEncodingPointerPos) { - if (!HandleCursorPos(rect.r.x, rect.r.y)) { - return False; + last_filexfer = time(NULL); + /*fprintf(stderr, "last_filexfer-1: %d\n", last_filexfer); */ + + /* load first byte to send to Java be the FT msg number: */ + hdr[0] = rfbFileTransfer; + + /* this is to avoid XtAppProcessEvent() calls induce by our ReadFromRFBServer calls below: */ + skip_XtUpdateAll = 1; + if (!ReadFromRFBServer(&hdr[1], 11)) { + skip_XtUpdateAll = 0; + return False; + } + if (filexfer_sock >= 0) { + write(filexfer_sock, hdr, 12); + } else { + fprintf(stderr, "filexfer_sock closed, discarding 12 bytes\n"); + } + if (db) fprintf(stderr, "\n"); + if (db) fprintf(stderr, "Got rfbFileTransfer hdr\n"); + if (db > 1) write(2, hdr, 12); + + if (db) { + int i; + fprintf(stderr, "HFX HDR:"); + for (i=0; i < 12; i++) { + fprintf(stderr, " %d", (int) hdr[i]); + } + fprintf(stderr, "\n"); } - continue; - } - if ((rect.r.x + rect.r.w > si.framebufferWidth) || - (rect.r.y + rect.r.h > si.framebufferHeight)) - { - fprintf(stderr,"Rect too large: %dx%d at (%d, %d)\n", - rect.r.w, rect.r.h, rect.r.x, rect.r.y); - return False; + if (hdr[1] == rfbEndOfFile) { + goto read_no_more; + } else if (hdr[1] == rfbAbortFileTransfer) { + goto read_no_more; } - if (rect.r.h * rect.r.w == 0) { - fprintf(stderr,"Zero size rect - ignoring\n"); - continue; - } + if (hdr[1] == rfbDirPacket && hdr[3] == rfbADirectory) { + + } + + len = (hdr[8] << 24) | (hdr[9] << 16) | (hdr[10] << 8) | hdr[11]; + if (db) fprintf(stderr, "Got rfbFileTransfer: len1 %u\n", len); + if (len > 0) { + if (!ReadFromRFBServer(fxfer, len)) { + skip_XtUpdateAll = 0; + return False; + } + if (db > 1) write(2, fxfer, len); + if (len >= 12 && hdr[1] == rfbDirPacket) { + /* try to guess if x11vnc or not... */ + if (db) { + int i; + fprintf(stderr, "HFX DIR PKT (attr, timeL, timeH):"); + for (i=0; i < 12; i++) { + fprintf(stderr, " %d", (unsigned char) fxfer[i]); + } + fprintf(stderr, "\n"); + } + if (hdr[2] == 1) { + int dattr = (unsigned char) fxfer[0]; + int timeL1 = (unsigned char) fxfer[4]; + int timeL2 = (unsigned char) fxfer[5]; + int timeL3 = (unsigned char) fxfer[6]; + int timeL4 = (unsigned char) fxfer[7]; + int timeH1 = (unsigned char) fxfer[8]; + int timeH2 = (unsigned char) fxfer[9]; + int timeH3 = (unsigned char) fxfer[10]; + int timeH4 = (unsigned char) fxfer[11]; + if (dattr != 0) { + if (timeH1 == 0 && timeH2 == 0 && timeH3 == 0 && timeH4 == 0) { + if ((timeL1 != 0 || timeL2 != 0) && timeL3 != 0 && timeL4 != 0) { + if (!guess_x11vnc) fprintf(stderr, "guessed x11vnc server\n"); + guess_x11vnc = 1; + } + } + } + } + } + if (db && 0) fprintf(stderr, "\n"); + if (filexfer_sock >= 0) { + write(filexfer_sock, fxfer, len); + } else { + fprintf(stderr, "filexfer_sock closed, discarding %d bytes\n", len); + } + } + + len = (hdr[4] << 24) | (hdr[5] << 16) | (hdr[6] << 8) | hdr[7]; + if (db) fprintf(stderr, "Got rfbFileTransfer: len2 %u\n", len); + +#if 0 + if (hdr[1] == rfbFileHeader && len != rfbRErrorCmd) +#else + /* the extra 4 bytes get send on rfbRErrorCmd as well. */ + if (hdr[1] == rfbFileHeader) { +#endif + int is_err = 0; + if (len == rfbRErrorCmd) { + is_err = 1; + } + if (db) fprintf(stderr, "Got rfbFileTransfer: rfbFileHeader\n"); + if (is_err && guess_x11vnc) { + fprintf(stderr, "rfbRErrorCmd x11vnc skip read 4 bytes.\n"); + goto read_no_more; + } + len = 4; + if (!ReadFromRFBServer(fxfer, len)) { + skip_XtUpdateAll = 0; + return False; + } + if (db > 1) write(2, fxfer, len); + if (db && 0) fprintf(stderr, "\n"); + if (is_err) { + fprintf(stderr, "rfbRErrorCmd skip write 4 bytes.\n"); + goto read_no_more; + } + if (filexfer_sock >= 0) { + write(filexfer_sock, fxfer, len); + } else { + fprintf(stderr, "filexfer_sock closed, discarding %d bytes\n", len); + } + } - /* If RichCursor encoding is used, we should prevent collisions - between framebuffer updates and cursor drawing operations. */ - SoftCursorLockArea(rect.r.x, rect.r.y, rect.r.w, rect.r.h); + read_no_more: - switch (rect.encoding) { + if (filexfer_sock < 0) { + int stop = 0; + static time_t last_stop = 0; +#if 0 + /* this isn't working */ + if (hdr[1] == rfbFilePacket || hdr[1] == rfbFileHeader) { + fprintf(stderr, "filexfer_sock closed, trying to abort receive\n"); + stop = 1; + } +#endif + if (stop && time(NULL) > last_stop+1) { + unsigned char rpl[12]; + int k; + rpl[0] = rfbFileTransfer; + rpl[1] = rfbAbortFileTransfer; + for (k=2; k < 12; k++) { + rpl[k] = 0; + } + WriteExact(rfbsock, rpl, 12); + last_stop = time(NULL); + } + } - case rfbEncodingRaw: + if (db) fprintf(stderr, "Got rfbFileTransfer done.\n"); + skip_XtUpdateAll = 0; - bytesPerLine = rect.r.w * myFormat.bitsPerPixel / 8; - linesToRead = BUFFER_SIZE / bytesPerLine; + if (db) fprintf(stderr, "CFX: B\n"); + CheckFileXfer(); +/*fprintf(stderr, "Out HandleFileXfer\n"); */ + return True; +} - while (rect.r.h > 0) { - if (linesToRead > rect.r.h) - linesToRead = rect.r.h; +/* + * HandleRFBServerMessage. + */ - if (!ReadFromRFBServer(buffer,bytesPerLine * linesToRead)) - return False; - CopyDataToScreen(buffer, rect.r.x, rect.r.y, rect.r.w, - linesToRead); +Bool +HandleRFBServerMessage() +{ + static int db = -1; + rfbServerToClientMsg msg; - rect.r.h -= linesToRead; - rect.r.y += linesToRead; + if (db < 0) { + if (getenv("DEBUG_RFB_SMSG")) { + db = 1; + } else { + db = 0; + } + } + if (!ReadFromRFBServer((char *)&msg, 1)) { + return False; } - break; + if (appData.ultraDSM) { + if (!ReadFromRFBServer((char *)&msg, 1)) { + return False; + } + } + +/*fprintf(stderr, "msg.type: %d\n", msg.type); */ - case rfbEncodingCopyRect: - { - rfbCopyRect cr; - - if (!ReadFromRFBServer((char *)&cr, sz_rfbCopyRect)) - return False; - - cr.srcX = Swap16IfLE(cr.srcX); - cr.srcY = Swap16IfLE(cr.srcY); - - /* If RichCursor encoding is used, we should extend our - "cursor lock area" (previously set to destination - rectangle) to the source rectangle as well. */ - SoftCursorLockArea(cr.srcX, cr.srcY, rect.r.w, rect.r.h); - - if (appData.copyRectDelay != 0) { - XFillRectangle(dpy, desktopWin, srcGC, cr.srcX, cr.srcY, - rect.r.w, rect.r.h); - XFillRectangle(dpy, desktopWin, dstGC, rect.r.x, rect.r.y, - rect.r.w, rect.r.h); - XSync(dpy,False); - usleep(appData.copyRectDelay * 1000); - XFillRectangle(dpy, desktopWin, dstGC, rect.r.x, rect.r.y, - rect.r.w, rect.r.h); - XFillRectangle(dpy, desktopWin, srcGC, cr.srcX, cr.srcY, - rect.r.w, rect.r.h); + if (msg.type == rfbFileTransfer) { + return HandleFileXfer(); } - XCopyArea(dpy, desktopWin, desktopWin, gc, cr.srcX, cr.srcY, - rect.r.w, rect.r.h, rect.r.x, rect.r.y); + switch (msg.type) { - break; - } + case rfbSetColourMapEntries: + { + int i; + CARD16 rgb[3]; + XColor xc; - case rfbEncodingRRE: - { - switch (myFormat.bitsPerPixel) { - case 8: - if (!HandleRRE8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) - return False; - break; - case 16: - if (!HandleRRE16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) - return False; - break; - case 32: - if (!HandleRRE32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) - return False; - break; + if (!ReadFromRFBServer(((char *)&msg) + 1, sz_rfbSetColourMapEntriesMsg - 1)) { + return False; } - break; - } - case rfbEncodingCoRRE: - { - switch (myFormat.bitsPerPixel) { - case 8: - if (!HandleCoRRE8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) - return False; - break; - case 16: - if (!HandleCoRRE16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) - return False; - break; - case 32: - if (!HandleCoRRE32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) - return False; - break; + msg.scme.firstColour = Swap16IfLE(msg.scme.firstColour); + msg.scme.nColours = Swap16IfLE(msg.scme.nColours); + + for (i = 0; i < msg.scme.nColours; i++) { + if (!ReadFromRFBServer((char *)rgb, 6)) { + return False; + } + xc.pixel = msg.scme.firstColour + i; + xc.red = Swap16IfLE(rgb[0]); + xc.green = Swap16IfLE(rgb[1]); + xc.blue = Swap16IfLE(rgb[2]); + if (appData.useGreyScale) { + int ave = (xc.red + xc.green + xc.blue)/3; + xc.red = ave; + xc.green = ave; + xc.blue = ave; + } + xc.flags = DoRed|DoGreen|DoBlue; + XStoreColor(dpy, cmap, &xc); } + break; - } + } + + case rfbFramebufferUpdate: + { + rfbFramebufferUpdateRectHeader rect; + int linesToRead; + int bytesPerLine; + int i; + + int area_copyrect = 0; + int area_tight = 0; + int area_zrle = 0; + int area_raw = 0; + static int rdb = -1; + static int delay_sync = -1; + static int delay_sync_env = -1; + int try_delay_sync = 0; + int cnt_pseudo = 0; + int cnt_image = 0; + + int skip_incFBU = 0; + + if (db) fprintf(stderr, "FBU-0: %.6f\n", dnow()); + if (rdb < 0) { + if (getenv("SSVNC_DEBUG_RECTS")) { + rdb = atoi(getenv("SSVNC_DEBUG_RECTS")); + } else { + rdb = 0; + } + } + if (delay_sync < 0) { + if (getenv("SSVNC_DELAY_SYNC")) { + delay_sync = atoi(getenv("SSVNC_DELAY_SYNC")); + delay_sync_env = delay_sync; + } else { + delay_sync = 0; + } + } + + sent_FBU = -1; - case rfbEncodingHextile: - { - switch (myFormat.bitsPerPixel) { - case 8: - if (!HandleHextile8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) - return False; - break; - case 16: - if (!HandleHextile16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) - return False; - break; - case 32: - if (!HandleHextile32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) - return False; - break; + if (appData.pipelineUpdates) { + /* turbovnc speed idea */ + XEvent ev; + memset(&ev, 0, sizeof(ev)); + ev.xclient.type = ClientMessage; + ev.xclient.window = XtWindow(desktop); + ev.xclient.message_type = XA_INTEGER; + ev.xclient.format = 8; + strcpy(ev.xclient.data.b, "SendRFBUpdate"); + XSendEvent(dpy, XtWindow(desktop), False, 0, &ev); } - break; - } - case rfbEncodingZlib: - { - switch (myFormat.bitsPerPixel) { - case 8: - if (!HandleZlib8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) - return False; - break; - case 16: - if (!HandleZlib16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) - return False; - break; - case 32: - if (!HandleZlib32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) - return False; - break; + if (!ReadFromRFBServer(((char *)&msg.fu) + 1, sz_rfbFramebufferUpdateMsg - 1)) { + return False; } - break; - } - case rfbEncodingTight: - { - switch (myFormat.bitsPerPixel) { - case 8: - if (!HandleTight8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) - return False; - break; - case 16: - if (!HandleTight16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) - return False; - break; - case 32: - if (!HandleTight32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) - return False; - break; + msg.fu.nRects = Swap16IfLE(msg.fu.nRects); + + if (rdb) fprintf(stderr, "Begin rect loop %d\n", msg.fu.nRects); + + if (delay_sync) { + try_delay_sync = 1; + } else { + if (delay_sync_env != -1 && delay_sync_env == 0) { + ; + } else if (appData.yCrop > 0) { + ; + } else if (scale_factor_x > 0.0 && scale_factor_x != 1.0) { + ; + } else if (scale_factor_y > 0.0 && scale_factor_y != 1.0) { + ; + } else { + static int msg = 0; + /* fullScreen? */ + /* useXserverBackingStore? */ + /* useX11Cursor & etc? */ + /* scrollbars? */ + if (!msg) { + fprintf(stderr, "enabling 'delay_sync' mode for faster local drawing,\ndisable via env SSVNC_DELAY_SYNC=0 if there are painting errors.\n"); + msg = 1; + } + try_delay_sync = 1; + } + } + if (try_delay_sync) { + skip_maybe_sync = 1; + } +#define STOP_DELAY_SYNC \ + if (try_delay_sync) { \ + if (cnt_image && skip_maybe_sync) { \ + XSync(dpy, False); \ + } \ + try_delay_sync = 0; \ + skip_maybe_sync = 0; \ } - break; - } - default: - fprintf(stderr,"Unknown rect encoding %d\n", - (int)rect.encoding); - return False; - } + for (i = 0; i < msg.fu.nRects; i++) { + if (!ReadFromRFBServer((char *)&rect, sz_rfbFramebufferUpdateRectHeader)) { + return False; + } + + rect.encoding = Swap32IfLE(rect.encoding); + if (rect.encoding == rfbEncodingLastRect) { + break; + } + + rect.r.x = Swap16IfLE(rect.r.x); + rect.r.y = Swap16IfLE(rect.r.y); + rect.r.w = Swap16IfLE(rect.r.w); + rect.r.h = Swap16IfLE(rect.r.h); + + if (rdb > 1) fprintf(stderr, "nRects: %d i=%d enc: %d %dx%d+%d+%d\n", msg.fu.nRects, i, (int) rect.encoding, rect.r.w, rect.r.h, rect.r.x, rect.r.y); + + if (rect.encoding == rfbEncodingXCursor || rect.encoding == rfbEncodingRichCursor) { + cnt_pseudo++; + STOP_DELAY_SYNC + + if (db) fprintf(stderr, "FBU-Cur1 %.6f\n", dnow()); + if (!HandleCursorShape(rect.r.x, rect.r.y, rect.r.w, rect.r.h, rect.encoding)) { + return False; + } + if (db) fprintf(stderr, "FBU-Cur2 %.6f\n", dnow()); + continue; + } + + if (rect.encoding == rfbEncodingPointerPos) { + cnt_pseudo++; + STOP_DELAY_SYNC + if (db) fprintf(stderr, "FBU-Pos1 %.6f\n", dnow()); + if (0) fprintf(stderr, "CursorPos: %d %d / %d %d\n", rect.r.x, rect.r.y, rect.r.w, rect.r.h); + if (ultra_scale > 0) { + int f = ultra_scale; + if (!HandleCursorPos(rect.r.x/f, rect.r.y/f)) { + return False; + } + } else { + if (!HandleCursorPos(rect.r.x, rect.r.y)) { + return False; + } + } + if (db) fprintf(stderr, "FBU-Pos2 %.6f\n", dnow()); + continue; + } + if (rect.encoding == rfbEncodingNewFBSize) { + cnt_pseudo++; + STOP_DELAY_SYNC + if (appData.chatOnly) { + continue; + } + fprintf(stderr,"New Size: %dx%d at (%d, %d)\n", rect.r.w, rect.r.h, rect.r.x, rect.r.y); + si.framebufferWidth = rect.r.w; + si.framebufferHeight = rect.r.h; + /*fprintf(stderr, "si: %d %d\n", si.framebufferWidth, si.framebufferHeight); */ + ReDoDesktop(); + continue; + } + if (rdb) fprintf(stderr,"Rect: %dx%d at (%d, %d)\n", rect.r.w, rect.r.h, rect.r.x, rect.r.y); + cnt_image++; + + if (appData.ultraDSM) { + /* + * What a huge mess the UltraVNC DSM plugin is!!! + * We read and ignore their little "this much data" hint... + */ + switch (rect.encoding) + { + case rfbEncodingRaw: + case rfbEncodingRRE: + case rfbEncodingCoRRE: + case rfbEncodingHextile: + /*case rfbEncodingUltra: */ +/* case rfbEncodingZlib: */ + /*case rfbEncodingXOR_Zlib: */ + /*case rfbEncodingXORMultiColor_Zlib: */ + /*case rfbEncodingXORMonoColor_Zlib: */ + /*case rfbEncodingSolidColor: */ + case rfbEncodingTight: + case rfbEncodingZlibHex: + case rfbEncodingZRLE: + case rfbEncodingZYWRLE: + { + CARD32 discard; + ReadFromRFBServer((char *)&discard, sizeof(CARD32)); + } + break; + } + } + + if ((rect.r.x + rect.r.w > si.framebufferWidth) || + (rect.r.y + rect.r.h > si.framebufferHeight)) { + if (!appData.chatOnly) { + fprintf(stderr,"Rect too large: %dx%d at (%d, %d) encoding=%d\n", + rect.r.w, rect.r.h, rect.r.x, rect.r.y, (int) rect.encoding); + return False; + } + } + + if (rect.r.h * rect.r.w == 0) { + fprintf(stderr,"*** Warning *** Zero size rect: %dx%d+%d+%d encoding=%d\n", + rect.r.w, rect.r.h, rect.r.x, rect.r.y, (int) rect.encoding); + if (0) continue; + } + + /* If RichCursor encoding is used, we should prevent collisions + between framebuffer updates and cursor drawing operations. */ + if (db) fprintf(stderr, "FBU-SCL1 %.6f\n", dnow()); + + SoftCursorLockArea(rect.r.x, rect.r.y, rect.r.w, rect.r.h); + + if (db) fprintf(stderr, "FBU-SCL2 %.6f\n", dnow()); + + + switch (rect.encoding) { + + case rfbEncodingRaw: + + bytesPerLine = rect.r.w * myFormat.bitsPerPixel / 8; + linesToRead = BUFFER_SIZE / bytesPerLine; + + if (db) fprintf(stderr, "Raw: %dx%d+%d+%d\n", rect.r.w, rect.r.h, rect.r.x, rect.r.y); + area_raw += rect.r.w * rect.r.h; + + while (rect.r.h > 0) { + if (linesToRead > rect.r.h) { + linesToRead = rect.r.h; + } + + if (!ReadFromRFBServer(buffer,bytesPerLine * linesToRead)) { + return False; + } + + CopyDataToScreen(buffer, rect.r.x, rect.r.y, rect.r.w, linesToRead); + + rect.r.h -= linesToRead; + rect.r.y += linesToRead; + } + break; + + case rfbEncodingCopyRect: + { + rfbCopyRect cr; + + STOP_DELAY_SYNC + XSync(dpy, False); + + if (!ReadFromRFBServer((char *)&cr, sz_rfbCopyRect)) { + return False; + } + if (appData.chatOnly) { + break; + } + + cr.srcX = Swap16IfLE(cr.srcX); + cr.srcY = Swap16IfLE(cr.srcY); + + if (db) fprintf(stderr, "Copy: %dx%d+%d+%d\n", rect.r.w, rect.r.h, rect.r.x, rect.r.y); + area_copyrect += rect.r.w * rect.r.h; + + /* If RichCursor encoding is used, we should extend our + "cursor lock area" (previously set to destination + rectangle) to the source rectangle as well. */ + + if (db) fprintf(stderr, "FBU-SCL3 %.6f\n", dnow()); + + SoftCursorLockArea(cr.srcX, cr.srcY, rect.r.w, rect.r.h); + + if (db) fprintf(stderr, "FBU-SCL4 %.6f\n", dnow()); + + if (appData.copyRectDelay != 0) { + XFillRectangle(dpy, desktopWin, srcGC, cr.srcX, cr.srcY, rect.r.w, rect.r.h); + XFillRectangle(dpy, desktopWin, dstGC, rect.r.x, rect.r.y, rect.r.w, rect.r.h); + XSync(dpy,False); + usleep(appData.copyRectDelay * 1000); + XFillRectangle(dpy, desktopWin, dstGC, rect.r.x, rect.r.y, rect.r.w, rect.r.h); + XFillRectangle(dpy, desktopWin, srcGC, cr.srcX, cr.srcY, rect.r.w, rect.r.h); + } + + if (db) fprintf(stderr, "FBU-CPA1 %.6f\n", dnow()); + if (!appData.useXserverBackingStore) { + copy_rect(rect.r.x, rect.r.y, rect.r.w, rect.r.h, cr.srcX, cr.srcY); + put_image(rect.r.x, rect.r.y, rect.r.x, rect.r.y, rect.r.w, rect.r.h, 0); + XSync(dpy, False); + } else { + XCopyArea(dpy, desktopWin, desktopWin, gc, cr.srcX, cr.srcY, + rect.r.w, rect.r.h, rect.r.x, rect.r.y); + } + if (db) fprintf(stderr, "FBU-CPA2 %.6f\n", dnow()); + + break; + } + + case rfbEncodingRRE: + { + switch (myFormat.bitsPerPixel) { + case 8: + if (!HandleRRE8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + case 16: + if (!HandleRRE16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + case 32: + if (!HandleRRE32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + } + break; + } + + case rfbEncodingCoRRE: + { + switch (myFormat.bitsPerPixel) { + case 8: + if (!HandleCoRRE8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + case 16: + if (!HandleCoRRE16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + case 32: + if (!HandleCoRRE32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + } + break; + } + + case rfbEncodingHextile: + { + switch (myFormat.bitsPerPixel) { + case 8: + if (!HandleHextile8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + case 16: + if (!HandleHextile16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + case 32: + if (!HandleHextile32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + } + break; + } + + case rfbEncodingZlib: + { + switch (myFormat.bitsPerPixel) { + case 8: + if (!HandleZlib8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + case 16: + if (!HandleZlib16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + case 32: + if (!HandleZlib32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + } + break; + } + + case rfbEncodingTight: + { + if (db) fprintf(stderr, "Tight: %dx%d+%d+%d\n", rect.r.w, rect.r.h, rect.r.x, rect.r.y); + area_tight += rect.r.w * rect.r.h; + if (db) fprintf(stderr, "FBU-TGH1 %.6f\n", dnow()); + + switch (myFormat.bitsPerPixel) { + case 8: + if (!HandleTight8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + case 16: + if (!HandleTight16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + case 32: + if (!HandleTight32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + } + if (db) fprintf(stderr, "FBU-TGH2 %.6f\n", dnow()); + break; + } + + /* runge adds zrle and zywrle: */ + case rfbEncodingZRLE: +#if DO_ZYWRLE + zywrle_level = 0; + case rfbEncodingZYWRLE: +#endif + { + if (db) fprintf(stderr, "ZRLE: %dx%d+%d+%d\n", rect.r.w, rect.r.h, rect.r.x, rect.r.y); + area_zrle += rect.r.w * rect.r.h; + switch (myFormat.bitsPerPixel) { + case 8: + if (!HandleZRLE8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + case 16: + if (myFormat.greenMax > 0x1f) { + if (!HandleZRLE16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + } else { + if (!HandleZRLE15(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + } + break; + case 32: + { + unsigned int maxColor=(myFormat.redMax<<myFormat.redShift)| + (myFormat.greenMax<<myFormat.greenShift)| + (myFormat.blueMax<<myFormat.blueShift); + static int ZRLE32 = -1; + + if (ZRLE32 < 0) { + /* for debugging or workaround e.g. BE display to LE */ + if (getenv("ZRLE32")) { + if (strstr(getenv("ZRLE32"), "24Up")) { + ZRLE32 = 3; + } else if (strstr(getenv("ZRLE32"), "24Down")) { + ZRLE32 = 2; + } else { + ZRLE32 = 1; + } + } else { + ZRLE32 = 0; + } + } + +if (db) fprintf(stderr, "maxColor: 0x%x mfbigEnding: %d\n", maxColor, myFormat.bigEndian); + + if (ZRLE32 == 1) { +if (db) fprintf(stderr, "HandleZRLE32\n"); + if (!HandleZRLE32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + } else if (ZRLE32 == 2) { +if (db) fprintf(stderr, "HandleZRLE24Down\n"); + if (!HandleZRLE24Down(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + } else if (ZRLE32 == 3) { +if (db) fprintf(stderr, "HandleZRLE24Up\n"); + if (!HandleZRLE24Up(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + } else if ((myFormat.bigEndian && (maxColor&0xff)==0) || (!myFormat.bigEndian && (maxColor&0xff000000)==0)) { +if (db) fprintf(stderr, "HandleZRLE24\n"); + if (!HandleZRLE24(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + } else if (!myFormat.bigEndian && (maxColor&0xff)==0) { +if (db) fprintf(stderr, "HandleZRLE24Up\n"); + if (!HandleZRLE24Up(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + } else if (myFormat.bigEndian && (maxColor&0xff000000)==0) { +if (db) fprintf(stderr, "HandleZRLE24Down\n"); + if (!HandleZRLE24Down(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + } else if (!HandleZRLE32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) { + return False; + } + break; + } + } + break; + } + + default: + fprintf(stderr,"Unknown rect encoding %d\n", (int)rect.encoding); + return False; + } - /* Now we may discard "soft cursor locks". */ - SoftCursorUnlockScreen(); - } + /* Now we may discard "soft cursor locks". */ + if (db) fprintf(stderr, "FBU-SUL1 %.6f\n", dnow()); + + SoftCursorUnlockScreen(); + + if (db) fprintf(stderr, "FBU-SUL2 %.6f\n", dnow()); + } + + if (try_delay_sync) { + skip_maybe_sync = 0; + } + + if (1 || area_copyrect) { + /* we always do this now for some reason... */ + if (db) fprintf(stderr, "FBU-XSN1 %.6f\n", dnow()); + XSync(dpy, False); + if (db) fprintf(stderr, "FBU-XSN2 %.6f\n", dnow()); + } + sent_FBU = 0; + /* + * we need to be careful since Xt events are processed + * usually in the middle of FBU. So we do any scheduled ones now + * which is pretty safe but not absolutely safe. + */ + if (do_format_change) { + cutover_format_change(); + do_format_change = 0; + SetVisualAndCmap(); + SetFormatAndEncodings(); + if (do_cursor_change) { + if (do_cursor_change == 1) { + DesktopCursorOff(); + } + do_cursor_change = 0; + } else { + SendFramebufferUpdateRequest(0, 0, si.framebufferWidth, + si.framebufferHeight, False); + skip_incFBU = 1; + } + } + if (do_fb_update != 0.0) { + if (dnow() > do_fb_update + 1.1) { + do_fb_update = 0.0; + SendFramebufferUpdateRequest(0, 0, si.framebufferWidth, + si.framebufferHeight, False); + } + } #ifdef MITSHM /* if using shared memory PutImage, make sure that the X server has @@ -1224,59 +3320,168 @@ mainly to avoid copyrect using invalid screen contents - not sure if we'd need it otherwise. */ - if (appData.useShm) - XSync(dpy, False); + if (appData.useShm) { + XSync(dpy, False); + } else #endif + { + /* we do it always now. */ + XSync(dpy, False); + } + + if (skip_XtUpdate || skip_incFBU) { + ; + } else if (appData.pipelineUpdates) { + ; + } else if (!SendIncrementalFramebufferUpdateRequest()) { + return False; + } - if (!SendIncrementalFramebufferUpdateRequest()) - return False; - - break; + break; } case rfbBell: { - Window toplevelWin; + Window toplevelWin; - XBell(dpy, 0); + if (appData.useBell) { + XBell(dpy, 0); + } + + if (appData.raiseOnBeep) { + toplevelWin = XtWindow(toplevel); + XMapRaised(dpy, toplevelWin); + } - if (appData.raiseOnBeep) { - toplevelWin = XtWindow(toplevel); - XMapRaised(dpy, toplevelWin); + break; } - break; - } + case rfbServerCutText: + { + if (!ReadFromRFBServer(((char *)&msg) + 1, sz_rfbServerCutTextMsg - 1)) { + return False; + } - case rfbServerCutText: - { - if (!ReadFromRFBServer(((char *)&msg) + 1, - sz_rfbServerCutTextMsg - 1)) - return False; + msg.sct.length = Swap32IfLE(msg.sct.length); - msg.sct.length = Swap32IfLE(msg.sct.length); + if (serverCutText) { + free(serverCutText); + } - if (serverCutText) - free(serverCutText); + serverCutText = malloc(msg.sct.length+1); - serverCutText = malloc(msg.sct.length+1); + if (!ReadFromRFBServer(serverCutText, msg.sct.length)) { + return False; + } - if (!ReadFromRFBServer(serverCutText, msg.sct.length)) - return False; + serverCutText[msg.sct.length] = 0; - serverCutText[msg.sct.length] = 0; + newServerCutText = True; - newServerCutText = True; + break; + } - break; - } + case rfbTextChat: + { + char *buffer = NULL; + if (!ReadFromRFBServer(((char *)&msg) + 1, sz_rfbTextChatMsg - 1)) { + return False; + } + msg.tc.length = Swap32IfLE(msg.tc.length); + switch(msg.tc.length) { + case rfbTextChatOpen: + if (appData.termChat) { + printChat("\n*ChatOpen*\n\nSend: ", True); + } else { + printChat("\n*ChatOpen*\n", True); + } + appData.chatActive = True; + break; + case rfbTextChatClose: + printChat("\n*ChatClose*\n", False); + appData.chatActive = False; + break; + case rfbTextChatFinished: + printChat("\n*ChatFinished*\n", False); + appData.chatActive = False; + break; + default: + buffer = (char *)malloc(msg.tc.length+1); + if (!ReadFromRFBServer(buffer, msg.tc.length)) { + free(buffer); + return False; + } + buffer[msg.tc.length] = '\0'; + appData.chatActive = True; + GotChatText(buffer, msg.tc.length); + free(buffer); + } + break; + } - default: - fprintf(stderr,"Unknown message type %d from VNC server\n",msg.type); - return False; - } + case rfbResizeFrameBuffer: + { + rfbResizeFrameBufferMsg rsmsg; + if (!ReadFromRFBServer(((char *)&rsmsg) + 1, sz_rfbResizeFrameBufferMsg - 1)) { + return False; + } + si.framebufferWidth = Swap16IfLE(rsmsg.framebufferWidth); + si.framebufferHeight = Swap16IfLE(rsmsg.framebufferHeight); + fprintf(stderr,"UltraVNC ReSize: %dx%d\n", si.framebufferWidth, si.framebufferHeight); + ReDoDesktop(); + break; + } - return True; + case rfbRestartConnection: + { + rfbRestartConnectionMsg rc; + int len; + char *rs_str; + char buf[5] = "\xff\xff\xff\xff"; + fprintf(stderr, "rfbRestartConnection. type=%d\n", (int) rc.type); + if (!ReadFromRFBServer((char *)&rc + 1, sz_rfbRestartConnectionMsg - 1)) { + return False; + } + len = Swap32IfLE(rc.length); + fprintf(stderr, "rfbRestartConnection. pad1=%d\n", (int) rc.pad1); + fprintf(stderr, "rfbRestartConnection. pad2=%d\n", (int) rc.pad2); + fprintf(stderr, "rfbRestartConnection. len=%d\n", len); + if (len) { + rs_str = (char *)malloc(2*len); + if (!ReadFromRFBServer(rs_str, len)) { + return False; + } + restart_session_pw = rs_str; + restart_session_len = len; + } + if (!WriteExact(rfbsock, buf, 4)) { + return False; + } + InitialiseRFBConnection(); + SetVisualAndCmap(); + SetFormatAndEncodings(); + DesktopCursorOff(); + SendFramebufferUpdateRequest(0, 0, si.framebufferWidth, si.framebufferHeight, False); + + break; + } + + default: + fprintf(stderr,"Unknown message type %d from VNC server\n",msg.type); + return False; + } + + if (appData.fileActive) { + if (filexfer_sock < 0 && filexfer_listen < 0) { + appData.fileActive = False; + SendFramebufferUpdateRequest(0, 0, 1, 1, False); + } else { +/*fprintf(stderr, "CFX: A\n"); */ + CheckFileXfer(); + } + } + + return True; } @@ -1296,26 +3501,93 @@ #define CONCAT2(a,b) a##b #define CONCAT2E(a,b) CONCAT2(a,b) +#define CONCAT3(a,b,c) a##b##c +#define CONCAT3E(a,b,c) CONCAT3(a,b,c) + +static unsigned char* frameBuffer = NULL; +static int frameBufferLen = 0; + +#ifdef TURBOVNC +#include "turbovnc/turbojpeg.h" +tjhandle tjhnd=NULL; +static char *compressedData = NULL; +static char *uncompressedData = NULL; +#define CopyDataToImage CopyDataToScreen +static void turbovnc_FillRectangle(XGCValues *gcv, int rx, int ry, int rw, int rh) { + if (!appData.useXserverBackingStore) { + FillScreen(rx, ry, rw, rh, gcv->foreground); + } else { + XChangeGC(dpy, gc, GCForeground, gcv); + XFillRectangle(dpy, desktopWin, gc, rx, ry, rw, rh); + } +} +static void CopyImageToScreen(int x, int y, int w, int h) { + put_image(x, y, x, y, w, h, 0); +} +#endif + #define BPP 8 #include "rre.c" #include "corre.c" #include "hextile.c" #include "zlib.c" + +#ifdef TURBOVNC +#undef FillRectangle +#define FillRectangle turbovnc_FillRectangle +#include "turbovnc/tight.c" +#undef FillRectangle +#else #include "tight.c" +#endif + +#include "zrle.c" #undef BPP + #define BPP 16 #include "rre.c" #include "corre.c" #include "hextile.c" #include "zlib.c" + +#ifdef TURBOVNC +#undef FillRectangle +#define FillRectangle turbovnc_FillRectangle +#include "turbovnc/tight.c" +#undef FillRectangle +#else #include "tight.c" +#endif + +#include "zrle.c" +#define REALBPP 15 +#include "zrle.c" #undef BPP + #define BPP 32 #include "rre.c" #include "corre.c" #include "hextile.c" #include "zlib.c" + +#ifdef TURBOVNC +#undef FillRectangle +#define FillRectangle turbovnc_FillRectangle +#include "turbovnc/tight.c" +#undef FillRectangle +#else #include "tight.c" +#endif + +#include "zrle.c" +#define REALBPP 24 +#include "zrle.c" +#define REALBPP 24 +#define UNCOMP 8 +#include "zrle.c" +#define REALBPP 24 +#define UNCOMP -8 +#include "zrle.c" #undef BPP /* @@ -1325,23 +3597,27 @@ static void ReadConnFailedReason(void) { - CARD32 reasonLen; - char *reason = NULL; + CARD32 reasonLen; + char *reason = NULL; - if (ReadFromRFBServer((char *)&reasonLen, sizeof(reasonLen))) { - reasonLen = Swap32IfLE(reasonLen); - if ((reason = malloc(reasonLen)) != NULL && - ReadFromRFBServer(reason, reasonLen)) { - fprintf(stderr,"VNC connection failed: %.*s\n", (int)reasonLen, reason); - free(reason); - return; - } - } + if (ReadFromRFBServer((char *)&reasonLen, sizeof(reasonLen))) { + reasonLen = Swap32IfLE(reasonLen); + if ((reason = malloc(reasonLen)) != NULL && + ReadFromRFBServer(reason, reasonLen)) { + int len = (int) reasonLen < sizeof(msgbuf) - 10 ? (int) reasonLen : sizeof(msgbuf) - 10; + sprintf(msgbuf,"VNC connection failed: %.*s\n", len, reason); + wmsg(msgbuf, 1); + free(reason); + return; + } + } - fprintf(stderr, "VNC connection failed\n"); + sprintf(msgbuf, "VNC connection failed\n"); + wmsg(msgbuf, 1); - if (reason != NULL) - free(reason); + if (reason != NULL) { + free(reason); + } } /* @@ -1358,9 +3634,9 @@ " %s significant bit in each byte is leftmost on the screen.\n", (format->bigEndian ? "Most" : "Least")); } else { - fprintf(stderr," %d bits per pixel.\n",format->bitsPerPixel); + fprintf(stderr," %d bits per pixel. ",format->bitsPerPixel); if (format->bitsPerPixel != 8) { - fprintf(stderr," %s significant byte first in each pixel.\n", + fprintf(stderr,"%s significant byte first in each pixel.\n", (format->bigEndian ? "Most" : "Least")); } if (format->trueColour) { @@ -1462,4 +3738,3 @@ cinfo->src = &jpegSrcManager; } - diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/rre.c vnc_unixsrc/vncviewer/rre.c --- vnc_unixsrc.orig/vncviewer/rre.c 2000-06-11 08:00:53.000000000 -0400 +++ vnc_unixsrc/vncviewer/rre.c 2008-10-05 15:16:30.000000000 -0400 @@ -29,6 +29,18 @@ #define HandleRREBPP CONCAT2E(HandleRRE,BPP) #define CARDBPP CONCAT2E(CARD,BPP) +#define FillRectangle(x, y, w, h, color) \ + { \ + XGCValues _gcv; \ + _gcv.foreground = color; \ + if (!appData.useXserverBackingStore) { \ + FillScreen(x, y, w, h, _gcv.foreground); \ + } else { \ + XChangeGC(dpy, gc, GCForeground, &_gcv); \ + XFillRectangle(dpy, desktopWin, gc, x, y, w, h); \ + } \ + } + static Bool HandleRREBPP (int rx, int ry, int rw, int rh) { @@ -49,11 +61,19 @@ #if (BPP == 8) gcv.foreground = (appData.useBGR233 ? BGR233ToPixel[pix] : pix); #else +#if (BPP == 16) + gcv.foreground = (appData.useBGR565 ? BGR565ToPixel[pix] : pix); +#else gcv.foreground = pix; #endif +#endif +#if 0 XChangeGC(dpy, gc, GCForeground, &gcv); XFillRectangle(dpy, desktopWin, gc, rx, ry, rw, rh); +#else + FillRectangle(rx, ry, rw, rh, gcv.foreground); +#endif for (i = 0; i < hdr.nSubrects; i++) { if (!ReadFromRFBServer((char *)&pix, sizeof(pix))) @@ -70,13 +90,23 @@ #if (BPP == 8) gcv.foreground = (appData.useBGR233 ? BGR233ToPixel[pix] : pix); #else +#if (BPP == 16) + gcv.foreground = (appData.useBGR565 ? BGR565ToPixel[pix] : pix); +#else gcv.foreground = pix; #endif +#endif +#if 0 XChangeGC(dpy, gc, GCForeground, &gcv); XFillRectangle(dpy, desktopWin, gc, rx + subrect.x, ry + subrect.y, subrect.w, subrect.h); +#else + FillRectangle(rx + subrect.x, ry + subrect.y, subrect.w, subrect.h, gcv.foreground); +#endif } return True; } + +#undef FillRectangle diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/selection.c vnc_unixsrc/vncviewer/selection.c --- vnc_unixsrc.orig/vncviewer/selection.c 2004-03-03 04:11:52.000000000 -0500 +++ vnc_unixsrc/vncviewer/selection.c 2010-02-25 23:28:48.000000000 -0500 @@ -43,13 +43,16 @@ unsigned long* length, int* format); static void LoseSelection(Widget w, Atom *selection); -static Bool iAmSelectionOwner = False; +static Bool PrimarySelectionOwner = False; +static Bool ClipboardSelectionOwner = False; static Time prevSelectionTime = 0L; static Time cutBufferTime = 0L; #define TIME_LATER(a, b) ((a) != 0 && ((b) == 0 || (INT32)((a) - (b)) > 0)) +static Atom clipboard_atom = None; + /* * InitialiseSelection() must be called after realizing widgets (because * otherwise XtGetSelectionValue() fails). We register events on the root @@ -62,22 +65,28 @@ * available. */ -void -InitialiseSelection() -{ +static int dbg_sel = -1; + +void InitialiseSelection() { #if XtSpecificationRelease >= 6 - XtRegisterDrawable(dpy, DefaultRootWindow(dpy), toplevel); + XtRegisterDrawable(dpy, DefaultRootWindow(dpy), toplevel); #else - _XtRegisterWindow(DefaultRootWindow(dpy), toplevel); + _XtRegisterWindow(DefaultRootWindow(dpy), toplevel); #endif - XSelectInput(dpy, DefaultRootWindow(dpy), PropertyChangeMask); + XSelectInput(dpy, DefaultRootWindow(dpy), PropertyChangeMask); - XtAddRawEventHandler(toplevel, PropertyChangeMask, False, CutBufferChange, - NULL); + XtAddRawEventHandler(toplevel, PropertyChangeMask, False, CutBufferChange, NULL); - XtGetSelectionValue(toplevel, XA_PRIMARY, + clipboard_atom = XInternAtom(dpy, "CLIPBOARD", False); + + XtGetSelectionValue(toplevel, XA_PRIMARY, XInternAtom(dpy, "TIMESTAMP", False), GetInitialSelectionTimeCallback, NULL, CurrentTime); + + if (dbg_sel < 0) { + dbg_sel = 0; + if (getenv("SSVNC_DEBUG_SELECTION")) dbg_sel = 1; + } } @@ -93,13 +102,16 @@ Atom* selection, Atom* type, XtPointer value, unsigned long* length, int* format) { - if (value && *format == 32 && *length == 1) - prevSelectionTime = *(CARD32 *)value; - else - prevSelectionTime = 0L; - - if (value) - XtFree(value); + if (value && *format == 32 && *length == 1) { + prevSelectionTime = *(CARD32 *)value; + } else { + prevSelectionTime = 0L; + } + + if (value) { + XtFree(value); + } + if (w || clientData || selection || type || value || length || format) {} } @@ -121,26 +133,30 @@ void SelectionToVNC(Widget w, XEvent *event, String *params, Cardinal *num_params) { - Bool always = False; + Bool always = appData.sendAlways; + Atom sendsel = XA_PRIMARY; - if (*num_params != 0) { - if (strcmp(params[0],"always") == 0) { - always = True; - } else if (strcmp(params[0],"new") == 0) { - always = False; - } else { - fprintf(stderr,"Invalid params: SelectionToVNC(always|new)\n"); - return; - } - } - - if (always) { - XtGetSelectionValue(w, XA_PRIMARY, XA_STRING, GetSelectionCallback, NULL, - TimeFromEvent(event)); - } else { - XtGetSelectionValue(w, XA_PRIMARY, XInternAtom(dpy, "TIMESTAMP", False), - GetSelectionTimeCallback, NULL, TimeFromEvent(event)); - } + if (*num_params != 0) { + if (strcmp(params[0],"always") == 0) { + always = True; + } else if (strcmp(params[0],"new") == 0) { + always = False; + } else { + fprintf(stderr,"Invalid params: SelectionToVNC(always|new)\n"); + return; + } + } + if (appData.sendClipboard && clipboard_atom != None) { + sendsel = clipboard_atom; + } + if (dbg_sel) fprintf(stderr, "SelectionToVNC %s\n", sendsel == XA_PRIMARY ? "PRIMARY" : "CLIPBOARD"); + + if (always) { + XtGetSelectionValue(w, sendsel, XA_STRING, GetSelectionCallback, NULL, TimeFromEvent(event)); + } else { + XtGetSelectionValue(w, sendsel, XInternAtom(dpy, "TIMESTAMP", False), GetSelectionTimeCallback, NULL, TimeFromEvent(event)); + } + if (w || event || params || num_params) {} } @@ -158,10 +174,13 @@ int len = *length; char *str = (char *)value; - if (str) - SendClientCutText(str, len); - else - SendCutBuffer(); + if (str) { + if (dbg_sel) fprintf(stderr, "SendClientCutText len: %d\n", len); + SendClientCutText(str, len); + } else if (!getenv("VNCVIEWER_NO_CUTBUFFER")) { + SendCutBuffer(); + } + if (w || clientData || selection || type || value || length || format) {} } @@ -180,26 +199,24 @@ Atom* type, XtPointer value, unsigned long* length, int* format) { - if (value && *format == 32 && *length == 1) { + if (value && *format == 32 && *length == 1) { + Time t = *(CARD32 *)value; - Time t = *(CARD32 *)value; - - if (TIME_LATER(t, prevSelectionTime)) { - prevSelectionTime = t; - XtGetSelectionValue(w, XA_PRIMARY, XA_STRING, GetSelectionCallback, NULL, - CurrentTime); - } - - } else { - - if (TIME_LATER(cutBufferTime, prevSelectionTime)) { - prevSelectionTime = cutBufferTime; - SendCutBuffer(); - } - } - - if (value) - XtFree(value); + if (TIME_LATER(t, prevSelectionTime)) { + prevSelectionTime = t; + XtGetSelectionValue(w, XA_PRIMARY, XA_STRING, GetSelectionCallback, NULL, CurrentTime); + } + } else if (!getenv("VNCVIEWER_NO_CUTBUFFER")) { + if (TIME_LATER(cutBufferTime, prevSelectionTime)) { + prevSelectionTime = cutBufferTime; + SendCutBuffer(); + } + } + + if (value) { + XtFree(value); + } + if (w || clientData || selection || type || value || length || format) {} } @@ -209,16 +226,17 @@ */ static void -SendCutBuffer() -{ - char *str; - int len; +SendCutBuffer() { + char *str; + int len; - str = XFetchBytes(dpy, &len); - if (!str) return; + if (dbg_sel) fprintf(stderr, "SendCutBuffer len: %d\n", len); - SendClientCutText(str, len); - XFree(str); + str = XFetchBytes(dpy, &len); + if (!str) return; + + SendClientCutText(str, len); + XFree(str); } @@ -230,10 +248,12 @@ static void CutBufferChange(Widget w, XtPointer ptr, XEvent *ev, Boolean *cont) { - if (ev->type != PropertyNotify || ev->xproperty.atom != XA_CUT_BUFFER0) - return; + if (ev->type != PropertyNotify || ev->xproperty.atom != XA_CUT_BUFFER0) { + return; + } - cutBufferTime = ev->xproperty.time; + cutBufferTime = ev->xproperty.time; + if (w || ptr || cont) {} } @@ -249,36 +269,69 @@ void SelectionFromVNC(Widget w, XEvent *event, String *params, Cardinal *num_params) { - Bool always = False; - Time t = TimeFromEvent(event); - - if (*num_params != 0) { - if (strcmp(params[0],"always") == 0) { - always = True; - } else if (strcmp(params[0],"new") == 0) { - always = False; - } else { - fprintf(stderr,"Invalid params: SelectionFromVNC(always|new)\n"); - return; - } - } - - if (t == CurrentTime) { - fprintf(stderr,"Error in translations: SelectionFromVNC() must act on " - "event with time field\n"); - return; - } - - if (!serverCutText || (!always && !newServerCutText)) - return; - - newServerCutText = False; - - XStoreBytes(dpy, serverCutText, strlen(serverCutText)); - if (XtOwnSelection(desktop, XA_PRIMARY, t, ConvertSelection, LoseSelection, - NULL)) { - iAmSelectionOwner = True; - } + Bool always = False; + Time t = TimeFromEvent(event); + int hold_primary = 0; + int hold_clipboard = 0; + + if (dbg_sel) fprintf(stderr, "SelectionFromVNC\n"); + + if (*num_params != 0) { + if (strcmp(params[0],"always") == 0) { + always = True; + } else if (strcmp(params[0],"new") == 0) { + always = False; + } else { + fprintf(stderr,"Invalid params: SelectionFromVNC(always|new)\n"); + return; + } + } + + if (t == CurrentTime) { + fprintf(stderr,"Error in translations: SelectionFromVNC() must act on " + "event with time field\n"); + return; + } + + if (!serverCutText || (!always && !newServerCutText)) { + return; + } + + newServerCutText = False; + + if (appData.appShare) { + if (strstr(serverCutText, "X11VNC_APPSHARE_CMD:") == serverCutText) { + /* do something with it? */ + return; + } + } + + XStoreBytes(dpy, serverCutText, strlen(serverCutText)); + + if (appData.recvText == NULL) { + appData.recvText = strdup("both"); + } + if (!strcasecmp(appData.recvText, "primary")) { + hold_primary = 1; + } else if (!strcasecmp(appData.recvText, "clipboard")) { + hold_clipboard = 1; + } else { + hold_primary = hold_clipboard = 1; + } + + if (!hold_primary) { + ; + } else if (XtOwnSelection(desktop, XA_PRIMARY, t, ConvertSelection, LoseSelection, NULL)) { + PrimarySelectionOwner = True; + if (dbg_sel) fprintf(stderr, "Own PRIMARY\n"); + } + if (!hold_clipboard || clipboard_atom == None) { + ; + } else if (XtOwnSelection(desktop, clipboard_atom, t, ConvertSelection, LoseSelection, NULL)) { + ClipboardSelectionOwner = True; + if (dbg_sel) fprintf(stderr, "Own CLIPBOARD\n"); + } + if (w || event || params || num_params) {} } @@ -293,37 +346,36 @@ XtPointer* value, unsigned long* length, int* format) { - if (*target == XA_STRING && serverCutText != NULL) { - *type = XA_STRING; - *length = strlen(serverCutText); - *value = (XtPointer)XtMalloc(*length); - memcpy((char*)*value, serverCutText, *length); - *format = 8; - return True; - } + if (*target == XA_STRING && serverCutText != NULL) { + *type = XA_STRING; + *length = strlen(serverCutText); + *value = (XtPointer)XtMalloc(*length); + memcpy((char*)*value, serverCutText, *length); + *format = 8; + return True; + } - if (XmuConvertStandardSelection(w, CurrentTime, selection, target, type, + if (XmuConvertStandardSelection(w, CurrentTime, selection, target, type, (XPointer*)value, length, format)) { - if (*target == XInternAtom(dpy, "TARGETS", False)) { - /* add STRING to list of standard targets */ - Atom* targetP; - Atom* std_targets = (Atom*)*value; - unsigned long std_length = *length; - - *length = std_length + 1; - *value = (XtPointer)XtMalloc(sizeof(Atom)*(*length)); - targetP = *(Atom**)value; - *targetP++ = XA_STRING; - memmove((char*)targetP, (char*)std_targets, sizeof(Atom)*std_length); - XtFree((char*)std_targets); - *type = XA_ATOM; - *format = 32; - return True; - } - - return True; - } - return False; + if (*target == XInternAtom(dpy, "TARGETS", False)) { + /* add STRING to list of standard targets */ + Atom* targetP; + Atom* std_targets = (Atom*)*value; + unsigned long std_length = *length; + + *length = std_length + 1; + *value = (XtPointer)XtMalloc(sizeof(Atom)*(*length)); + targetP = *(Atom**)value; + *targetP++ = XA_STRING; + memmove((char*)targetP, (char*)std_targets, sizeof(Atom)*std_length); + XtFree((char*)std_targets); + *type = XA_ATOM; + *format = 32; + return True; + } + return True; + } + return False; } @@ -332,7 +384,13 @@ */ static void -LoseSelection(Widget w, Atom *selection) -{ - iAmSelectionOwner = False; +LoseSelection(Widget w, Atom *selection) { + if (*selection == XA_PRIMARY) { + if (dbg_sel) fprintf(stderr, "lost PRIMARY\n"); + PrimarySelectionOwner = False; + } else if (clipboard_atom != None && *selection == clipboard_atom) { + if (dbg_sel) fprintf(stderr, "lost CLIPBOARD\n"); + ClipboardSelectionOwner = False; + } + if (w) {} } diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/shm.c vnc_unixsrc/vncviewer/shm.c --- vnc_unixsrc.orig/vncviewer/shm.c 2000-06-11 08:00:53.000000000 -0400 +++ vnc_unixsrc/vncviewer/shm.c 2010-02-25 23:40:58.000000000 -0500 @@ -30,71 +30,113 @@ static Bool caughtShmError = False; static Bool needShmCleanup = False; -void -ShmCleanup() -{ - fprintf(stderr,"ShmCleanup called\n"); - if (needShmCleanup) { - shmdt(shminfo.shmaddr); - shmctl(shminfo.shmid, IPC_RMID, 0); - needShmCleanup = False; - } +static int ShmCreationXErrorHandler(Display *dpy, XErrorEvent *error) { + caughtShmError = True; + if (dpy || error) {} + return 0; } -static int -ShmCreationXErrorHandler(Display *dpy, XErrorEvent *error) -{ - caughtShmError = True; - return 0; +void ShmDetach() { + if (needShmCleanup) { + XErrorHandler oldXErrorHandler = XSetErrorHandler(ShmCreationXErrorHandler); + fprintf(stderr,"ShmDetach called.\n"); + XShmDetach(dpy, &shminfo); + XSync(dpy, False); + XSetErrorHandler(oldXErrorHandler); + } } -XImage * -CreateShmImage() -{ - XImage *image; - XErrorHandler oldXErrorHandler; - - if (!XShmQueryExtension(dpy)) - return NULL; - - image = XShmCreateImage(dpy, vis, visdepth, ZPixmap, NULL, &shminfo, - si.framebufferWidth, si.framebufferHeight); - if (!image) return NULL; - - shminfo.shmid = shmget(IPC_PRIVATE, - image->bytes_per_line * image->height, - IPC_CREAT|0777); +void ShmCleanup() { + if (needShmCleanup) { + fprintf(stderr,"ShmCleanup called.\n"); + XSync(dpy, False); + shmdt(shminfo.shmaddr); + shmctl(shminfo.shmid, IPC_RMID, 0); - if (shminfo.shmid == -1) { - XDestroyImage(image); - return NULL; - } - - shminfo.shmaddr = image->data = shmat(shminfo.shmid, 0, 0); - - if (shminfo.shmaddr == (char *)-1) { - XDestroyImage(image); - shmctl(shminfo.shmid, IPC_RMID, 0); - return NULL; - } + needShmCleanup = False; + } +} - shminfo.readOnly = True; +Bool UsingShm() { + return needShmCleanup; +} - oldXErrorHandler = XSetErrorHandler(ShmCreationXErrorHandler); - XShmAttach(dpy, &shminfo); - XSync(dpy, False); - XSetErrorHandler(oldXErrorHandler); +int scale_round(int len, double fac); +extern int scale_x, scale_y; +extern double scale_factor_x, scale_factor_y; - if (caughtShmError) { - XDestroyImage(image); - shmdt(shminfo.shmaddr); - shmctl(shminfo.shmid, IPC_RMID, 0); - return NULL; - } +XImage * +CreateShmImage(int do_ycrop) +{ + XImage *image; + XErrorHandler oldXErrorHandler; + int ymax = si.framebufferHeight; + int xmax = si.framebufferWidth; + + if (!XShmQueryExtension(dpy)) { + return NULL; + } + if (!appData.useShm) { + return NULL; + } + if (do_ycrop == -1) { + /* kludge to test for shm prescence */ + return (XImage *) 0x1; + } + + if (do_ycrop) { + ymax = appData.yCrop; + } + + if (scale_x > 0) { + xmax = scale_round(xmax, scale_factor_x); + ymax = scale_round(ymax, scale_factor_y); + } + + image = XShmCreateImage(dpy, vis, visdepth, ZPixmap, NULL, &shminfo, xmax, ymax); + if (!image) { + return NULL; + } + + shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height, IPC_CREAT|0777); + + if (shminfo.shmid == -1) { + XDestroyImage(image); + if (0) fprintf(stderr, "CreateShmImage: destroyed 'image' (1)\n"); + return NULL; + } + + shminfo.shmaddr = image->data = shmat(shminfo.shmid, 0, 0); + + if (shminfo.shmaddr == (char *)-1) { + XDestroyImage(image); +#if 0 + fprintf(stderr, "CreateShmImage: destroyed 'image' (2)\n"); +#endif + shmctl(shminfo.shmid, IPC_RMID, 0); + return NULL; + } + + shminfo.readOnly = True; + + oldXErrorHandler = XSetErrorHandler(ShmCreationXErrorHandler); + XShmAttach(dpy, &shminfo); + XSync(dpy, False); + XSetErrorHandler(oldXErrorHandler); + + if (caughtShmError) { + XDestroyImage(image); +#if 0 + fprintf(stderr, "CreateShmImage: destroyed 'image' (3)\n"); +#endif + shmdt(shminfo.shmaddr); + shmctl(shminfo.shmid, IPC_RMID, 0); + return NULL; + } - needShmCleanup = True; + needShmCleanup = True; - fprintf(stderr,"Using shared memory PutImage\n"); + fprintf(stderr,"Using shared memory (PutImage ycrop=%d, Size %dx%d)\n", do_ycrop, xmax, ymax); - return image; + return image; } diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/smake vnc_unixsrc/vncviewer/smake --- vnc_unixsrc.orig/vncviewer/smake 1969-12-31 19:00:00.000000000 -0500 +++ vnc_unixsrc/vncviewer/smake 2007-02-19 12:28:05.000000000 -0500 @@ -0,0 +1,11 @@ +#!/bin/sh + +PATH=`pwd`/../..:/usr/sfw/bin:/usr/ccs/bin:$PATH +export PATH +if [ "X$1" != "X" ]; then + "$@" +else + make + strip vncviewer + ls -l vncviewer +fi diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/sockets.c vnc_unixsrc/vncviewer/sockets.c --- vnc_unixsrc.orig/vncviewer/sockets.c 2001-01-14 22:54:18.000000000 -0500 +++ vnc_unixsrc/vncviewer/sockets.c 2010-04-18 11:41:07.000000000 -0400 @@ -22,17 +22,31 @@ */ #include <unistd.h> +#include <time.h> #include <sys/socket.h> #include <errno.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> +#include <sys/un.h> #include <netdb.h> #include <fcntl.h> #include <assert.h> #include <vncviewer.h> +#ifndef SOL_IPV6 +#ifdef IPPROTO_IPV6 +#define SOL_IPV6 IPPROTO_IPV6 +#endif +#endif + +/* Solaris (sysv?) needs INADDR_NONE */ +#ifndef INADDR_NONE +#define INADDR_NONE ((in_addr_t) 0xffffffff) +#endif + void PrintInHex(char *buf, int len); +extern void printChat(char *, Bool); Bool errorMessageOnReadFailure = True; @@ -56,31 +70,396 @@ */ static Bool rfbsockReady = False; +static Bool xfrsockReady = False; +static XtInputId rfbsockId = 0; +static XtInputId xfrsockId = 0; +static int do_rfbsockId = 0; +static int do_xfrsockId = 0; + static void rfbsockReadyCallback(XtPointer clientData, int *fd, XtInputId *id) { - rfbsockReady = True; - XtRemoveInput(*id); + rfbsockReady = True; +#if 0 + XtRemoveInput(*id); +#endif + XtRemoveInput(rfbsockId); + if (do_xfrsockId) { + XtRemoveInput(xfrsockId); + } + if (clientData || fd || id) {} } static void -ProcessXtEvents() +xfrsockReadyCallback(XtPointer clientData, int *fd, XtInputId *id) { - rfbsockReady = False; - XtAppAddInput(appContext, rfbsock, (XtPointer)XtInputReadMask, - rfbsockReadyCallback, NULL); - while (!rfbsockReady) { - XtAppProcessEvent(appContext, XtIMAll); - } + xfrsockReady = True; + XtRemoveInput(xfrsockId); + if (do_rfbsockId) { + XtRemoveInput(rfbsockId); + } + if (clientData || fd || id) {} +} + + +extern int skip_XtUpdate; +extern int skip_XtUpdateAll; +extern int filexfer_sock, filexfer_listen; +extern time_t start_listen; +extern void CheckTextInput(void); +extern time_t last_filexfer; + +static char fxfer[65536]; +int fxfer_size = 65536; + +int rfbsock_is_ready(void) { + fd_set fds; + struct timeval tv; + + if (rfbsock < 0) { + return 0; + } + FD_ZERO(&fds); + FD_SET(rfbsock,&fds); + tv.tv_sec = 0; + tv.tv_usec = 0; + if (select(rfbsock+1, &fds, NULL, NULL, &tv) > 0) { + if (FD_ISSET(rfbsock, &fds)) { + return 1; + } + } + return 0; +} + +time_t filexfer_start = 0; + +void CheckFileXfer() { + fd_set fds; + struct timeval tv; + int i, icnt = 0, igot = 0, bytes0 = 0, bytes = 0, grace = 0, n, list = 0; + int db = 0; + + if (!appData.fileActive || (filexfer_sock < 0 && filexfer_listen < 0)) { + return; + } + + if (filexfer_listen >= 0 && time(NULL) > start_listen + 30) { + fprintf(stderr, "filexfer closing aging listen socket.\n"); + close(filexfer_listen); + filexfer_listen = -1; + return; + } +if (0) fprintf(stderr, "In CheckFileXfer\n"); + + if (filexfer_listen >=0) { + n = filexfer_listen; + list = 1; + } else { + n = filexfer_sock; + } + + while (1) { + icnt++; + FD_ZERO(&fds); + FD_SET(n,&fds); + tv.tv_sec = 0; + tv.tv_usec = 0; + if (select(n+1, &fds, NULL, NULL, &tv) > 0) { + if (FD_ISSET(n, &fds)) { + if (list) { + if (filexfer_sock >= 0) { + fprintf(stderr, "filexfer close stale(?) filexfer_sock.\n"); + close(filexfer_sock); + filexfer_sock = -1; + } + filexfer_sock = AcceptTcpConnection(filexfer_listen); + if (filexfer_sock >= 0) { + fprintf(stderr, "filexfer accept OK.\n"); + close(filexfer_listen); + filexfer_listen = -1; + filexfer_start = last_filexfer = time(NULL); + } else { + fprintf(stderr, "filexfer accept failed.\n"); + } + break; + } else { + ssize_t rn; + unsigned char hdr[12]; + unsigned int len; + if (db) fprintf(stderr, "try read filexfer...\n"); + if (hdr || len || i) {} +#if 1 + rn = read(n, fxfer, 1*8192); +if (db) { + int i; + fprintf(stderr, "CFX HDR:"); + for (i=0; i < 12; i++) { + fprintf(stderr, " %d", (int) fxfer[i]); + } + fprintf(stderr, " ?\n"); +} + if (0 || db) fprintf(stderr, "filexfer read[%d] %d.\n", icnt, rn); + if (rn < 0) { + fprintf(stderr, "filexfer bad read: %d\n", errno); + break; + } else if (rn == 0) { + fprintf(stderr, "filexfer gone.\n"); + close(n); + filexfer_sock = -1; + last_filexfer = time(NULL); +#if 0 + fprintf(stderr, "last_filexfer-2a: %d\n", last_filexfer); +#endif + appData.fileActive = False; + SendFramebufferUpdateRequest(0, 0, 1, 1, False); + return; + } else if (rn > 0) { + if (db > 1) write(2, fxfer, rn); + if (db) fprintf(stderr, "\n"); + bytes += rn; + last_filexfer = time(NULL); +#if 0 + fprintf(stderr, "last_filexfer-2b: %d\n", last_filexfer); +#endif + + if (0) { + /* WE TRY TO FIX THIS IN THE JAVA NOW */ + if (appData.ultraDSM) { + unsigned char msg = rfbFileTransfer; + unsigned char hdc = (unsigned char) fxfer[0]; + if (msg == hdc) { + /* cross your fingers... */ + WriteExact(rfbsock, (char *)&msg, 1); + } + } + } + if (!WriteExact(rfbsock, fxfer, rn)) { + return; + } + igot = 1; + } +#else + /* not working, not always 7 msg type. */ + rn = read(n, hdr, 12); + if (db) fprintf(stderr, "filexfer read %d.\n", rn); + if (rn == 0) { + fprintf(stderr, "filexfer gone.\n"); + close(n); + filexfer_sock = -1; + last_filexfer = time(NULL); + return; + } + if (rn == 12) { + len = (hdr[8] << 24) | (hdr[9] << 16) | (hdr[10] << 8) | hdr[11]; + if (db) fprintf(stderr, "n=%d len=%d\n", rn, len); + if (db > 1) write(2, hdr, rn); + if (db) fprintf(stderr, "\n"); + WriteExact(rfbsock, hdr, rn); + if (len > 0) { + rn = read(len, fxfer, len); + if (!WriteExact(rfbsock, fxfer, len)) { + last_filexfer = time(NULL); + return; + } + if (db > 1) write(2, fxfer, len); + } + if (db) fprintf(stderr, "\n"); + } else { + if (db) fprintf(stderr, "bad rn: %d\n", rn); + } + igot = 1; +#endif + } + } + } else { + if (bytes >= 8192) { + int ok = 0; + if (bytes0 == 0) { + ok = 1; + } else if (bytes >= bytes0 + 12) { + ok = 1; + } else if (grace < 20) { + ok = 1; + } + if (ok) { + grace++; + bytes0 = bytes; +#if 0 + fprintf(stderr, "grace: %d\n", grace); + /* forgot that this is about... */ +#endif + usleep(10 * 1000); + continue; + } + } + break; + } + } + if (igot) { + last_filexfer = time(NULL); +#if 0 + fprintf(stderr, "last_filexfer-2c: %d\n", last_filexfer); +#endif + } +#if 0 +fprintf(stderr, "Out CheckFileXfer\n"); +#endif + return; +} + +static void check_term_chat(void) { + fd_set fds; + struct timeval tv; + int i, igot = -1, n = fileno(stdin); + char strs[100][512]; + char buf[rfbTextMaxSize]; + + for (i=0; i < 100; i++) { + FD_ZERO(&fds); + FD_SET(n,&fds); + tv.tv_sec = 0; + tv.tv_usec = 0; + if (select(n+1, &fds, NULL, NULL, &tv) > 0) { + if (FD_ISSET(n, &fds)) { + fgets(strs[i], 512, stdin); + igot = i; + } else { + break; + } + } else { + break; + } + } + buf[0] = '\0'; + for (i=0; i <= igot; i++) { + if (strlen(buf) + strlen(strs[i]) < rfbTextMaxSize) { + strcat(buf, strs[i]); + } else { + SendTextChat(buf); + buf[0] = '0'; + } + } + if (buf[0] != '\0') { + SendTextChat(buf); + } + if (igot >= 0) printChat("Send: ", False); +} + +static time_t time_mark; +extern int delay_filexfer; +#include <sys/stat.h> + +extern double start_time; + +void ProcessXtEvents() +{ + int db = 0; + static int dyn = -1; + static int chat_was_active = 0; + int check_chat = 0; + + if (dyn < 0) { + struct stat sb; + if (getenv("USER") && !strcmp(getenv("USER"), "runge")) { + if (stat("/tmp/nodyn", &sb) == 0) { + putenv("NOFTFBUPDATES=1"); + unlink("/tmp/nodyn"); + } + } + if (getenv("NOFTFBUPDATES")) { + dyn = 0; + } else { + dyn = 1; + } + } + +#if 0 + if (0) fprintf(stderr, "ProcessXtEvents: %d %.4f\n", skip_XtUpdateAll, dnow() - start_time); +#endif + + if (skip_XtUpdateAll) { + return; + } + + /* text chat */ + if (appData.chatActive ) { + check_chat = 1; + } else if (chat_was_active) { + static double last_check = 0.0; + double now = dnow(); + if (now > last_check + 0.75) { + check_chat = 1; + last_check = now; + } + } + if (check_chat) { + if (appData.chatActive) { + chat_was_active = 1; + } + if (!appData.termChat) { + CheckTextInput(); + } else { + check_term_chat(); + } + } + + if (skip_XtUpdate) { + return; + } + + rfbsockReady = False; + xfrsockReady = False; + do_rfbsockId = 1; + rfbsockId = XtAppAddInput(appContext, rfbsock, (XtPointer)XtInputReadMask, + rfbsockReadyCallback, NULL); + + do_xfrsockId = 0; + if (filexfer_sock >= 0) { + do_xfrsockId = 1; + xfrsockId = XtAppAddInput(appContext, filexfer_sock, (XtPointer)XtInputReadMask, + xfrsockReadyCallback, NULL); + } + + time_mark = time(NULL); + + if (appData.fileActive) { + static int first = 1; + if (first) { + fprintf(stderr, "PXT: dynamic fb updates during filexfer: %d\n", dyn); + first = 0; + } + } + + if (db) fprintf(stderr, "XtAppAddInput: "); + while (!rfbsockReady && !xfrsockReady) { + int w = si.framebufferWidth; + int h = si.framebufferHeight; + if (db) fprintf(stderr, "."); + if (dyn && filexfer_sock >= 0 && time(NULL) > time_mark + delay_filexfer) { + SendFramebufferUpdateRequest(0, 0, w, h, False); + } + XtAppProcessEvent(appContext, XtIMAll); + } + if (db) fprintf(stderr, " done. r: %d x: %d\n", rfbsockReady, xfrsockReady); + + if (xfrsockReady) { + CheckFileXfer(); + } } Bool ReadFromRFBServer(char *out, unsigned int n) { +#if 0 + double start = dnow(), dn = n; +#endif if (n <= buffered) { memcpy(out, bufoutptr, n); bufoutptr += n; buffered -= n; +#if 0 +fprintf(stderr, "R0: %06d\n", (int) dn); +#endif return True; } @@ -119,6 +498,9 @@ memcpy(out, bufoutptr, n); bufoutptr += n; buffered -= n; +#if 0 +fprintf(stderr, "R1: %06d %06d %10.2f KB/sec\n", (int) dn, buffered+n, 1e-3 * (buffered+n)/(dnow() - start)); +#endif return True; } else { @@ -146,11 +528,16 @@ n -= i; } +#if 0 +fprintf(stderr, "R2: %06d %06d %10.2f KB/sec\n", (int) dn, (int) dn, 1e-3 * (dn)/(dnow() - start)); +#endif return True; } } +int currentMsg = -1; + /* * Write an exact number of bytes, and don't return until you've sent them. */ @@ -158,81 +545,321 @@ Bool WriteExact(int sock, char *buf, int n) { - fd_set fds; - int i = 0; - int j; - - while (i < n) { - j = write(sock, buf + i, (n - i)); - if (j <= 0) { - if (j < 0) { - if (errno == EWOULDBLOCK || errno == EAGAIN) { - FD_ZERO(&fds); - FD_SET(rfbsock,&fds); + fd_set fds; + int i = 0; + int j; + + if (appData.ultraDSM && currentMsg >= 0) { + /* this is for goofy UltraVNC DSM send RFB msg char twice: */ + unsigned char msg = (unsigned char) currentMsg; + currentMsg = -1; + if (!WriteExact(sock, (char *)&msg, sizeof(msg))) { + return False; + } + } + currentMsg = -1; - if (select(rfbsock+1, NULL, &fds, NULL, NULL) <= 0) { - fprintf(stderr,programName); - perror(": select"); - return False; - } - j = 0; - } else { - fprintf(stderr,programName); - perror(": write"); - return False; + while (i < n) { + j = write(sock, buf + i, (n - i)); + if (j <= 0) { + if (j < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + FD_ZERO(&fds); + FD_SET(rfbsock,&fds); + + if (select(rfbsock+1, NULL, &fds, NULL, NULL) <= 0) { + fprintf(stderr,programName); + perror(": select"); + return False; + } + j = 0; + } else { + fprintf(stderr,programName); + perror(": write"); + return False; + } + } else { + fprintf(stderr,"%s: write failed\n",programName); + return False; + } + } + i += j; } - } else { - fprintf(stderr,"%s: write failed\n",programName); - return False; - } - } - i += j; - } - return True; + return True; } +int +ConnectToUnixSocket(char *file) { + int sock; + struct sockaddr_un addr; + int i; + + memset(&addr, 0, sizeof(struct sockaddr_un)); + + addr.sun_family = AF_UNIX; + + for (i=0; i < 108; i++) { + addr.sun_path[i] = file[i]; + if (file[i] == '\0') { + break; + } + } + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + fprintf(stderr,programName); + perror(": ConnectToUnixSocket: socket"); + return -1; + } + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + fprintf(stderr, programName); + perror(": ConnectToUnixSocket: connect"); + close(sock); + return -1; + } + + return sock; +} + +char *ipv6_getipaddr(struct sockaddr *paddr, int addrlen) { +#if defined(AF_INET6) && defined(NI_NUMERICHOST) + char name[200]; + if (appData.noipv6) { + return strdup("unknown"); + } + if (getnameinfo(paddr, addrlen, name, sizeof(name), NULL, 0, NI_NUMERICHOST) == 0) { + return strdup(name); + } +#endif + if (paddr || addrlen) {} + return strdup("unknown"); +} + +char *ipv6_getnameinfo(struct sockaddr *paddr, int addrlen) { +#if defined(AF_INET6) + char name[200]; + if (appData.noipv6) { + return strdup("unknown"); + } + if (getnameinfo(paddr, addrlen, name, sizeof(name), NULL, 0, 0) == 0) { + return strdup(name); + } +#endif + if (paddr || addrlen) {} + return strdup("unknown"); +} + +int dotted_ip(char *host, int partial) { + int len, dots = 0; + char *p = host; + + if (!host) { + return 0; + } + + if (!isdigit((unsigned char) host[0])) { + return 0; + } + + len = strlen(host); + if (!partial && !isdigit((unsigned char) host[len-1])) { + return 0; + } + + while (*p != '\0') { + if (*p == '.') dots++; + if (*p == '.' || isdigit((unsigned char) (*p))) { + p++; + continue; + } + return 0; + } + if (!partial && dots != 3) { + return 0; + } + return 1; +} /* * ConnectToTcpAddr connects to the given TCP port. */ -int -ConnectToTcpAddr(unsigned int host, int port) -{ - int sock; - struct sockaddr_in addr; - int one = 1; - - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = host; +int ConnectToTcpAddr(const char *hostname, int port) { + int sock = -1, one = 1; + unsigned int host; + struct sockaddr_in addr; + + if (appData.noipv4) { + fprintf(stderr, "ipv4 is disabled via VNCVIEWER_NO_IPV4/-noipv4.\n"); + goto try6; + } - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) { - fprintf(stderr,programName); - perror(": ConnectToTcpAddr: socket"); - return -1; - } + if (!StringToIPAddr(hostname, &host)) { + fprintf(stderr, "Could not convert '%s' to ipv4 host address.\n", hostname); + goto try6; + } - if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - fprintf(stderr,programName); - perror(": ConnectToTcpAddr: connect"); - close(sock); - return -1; - } + memset(&addr, 0, sizeof(struct sockaddr_in)); - if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, - (char *)&one, sizeof(one)) < 0) { - fprintf(stderr,programName); - perror(": ConnectToTcpAddr: setsockopt"); - close(sock); - return -1; - } + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = host; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror("ConnectToTcpAddr[ipv4]: socket"); + sock = -1; + goto try6; + } + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("ConnectToTcpAddr[ipv4]: connect"); + close(sock); + sock = -1; + goto try6; + } + + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { + perror("ConnectToTcpAddr[ipv4]: setsockopt"); + close(sock); + sock = -1; + goto try6; + } - return sock; + if (sock >= 0) { + return sock; + } + + try6: + +#ifdef AF_INET6 + if (!appData.noipv6) { + int err; + struct addrinfo *ai; + struct addrinfo hints; + char service[32], *host2, *q; + + fprintf(stderr, "Trying ipv6 connection to '%s'\n", hostname); + + memset(&hints, 0, sizeof(hints)); + sprintf(service, "%d", port); + + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif +#ifdef AI_NUMERICSERV + hints.ai_flags |= AI_NUMERICSERV; +#endif + if (!strcmp(hostname, "localhost")) { + host2 = strdup("::1"); + } else if (!strcmp(hostname, "127.0.0.1")) { + host2 = strdup("::1"); + } else if (hostname[0] == '[') { + host2 = strdup(hostname+1); + } else { + host2 = strdup(hostname); + } + q = strrchr(host2, ']'); + if (q) { + *q = '\0'; + } + + err = getaddrinfo(host2, service, &hints, &ai); + if (err != 0) { + fprintf(stderr, "ConnectToTcpAddr[ipv6]: getaddrinfo[%d]: %s\n", err, gai_strerror(err)); + usleep(100 * 1000); + err = getaddrinfo(host2, service, &hints, &ai); + } + free(host2); + + if (err != 0) { + fprintf(stderr, "ConnectToTcpAddr[ipv6]: getaddrinfo[%d]: %s (2nd try)\n", err, gai_strerror(err)); + } else { + struct addrinfo *ap = ai; + while (ap != NULL) { + int fd = -1; + char *s = ipv6_getipaddr(ap->ai_addr, ap->ai_addrlen); + if (s) { + fprintf(stderr, "ConnectToTcpAddr[ipv6]: trying ip-addr: '%s'\n", s); + free(s); + } + if (appData.noipv4) { + struct sockaddr_in6 *s6ptr; + if (ap->ai_family != AF_INET6) { + fprintf(stderr, "ConnectToTcpAddr[ipv6]: skipping AF_INET address under VNCVIEWER_NO_IPV4/-noipv4\n"); + ap = ap->ai_next; + continue; + } +#ifdef IN6_IS_ADDR_V4MAPPED + s6ptr = (struct sockaddr_in6 *) ap->ai_addr; + if (IN6_IS_ADDR_V4MAPPED(&(s6ptr->sin6_addr))) { + fprintf(stderr, "ConnectToTcpAddr[ipv6]: skipping V4MAPPED address under VNCVIEWER_NO_IPV4/-noipv4\n"); + ap = ap->ai_next; + continue; + } +#endif + } + + fd = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol); + if (fd == -1) { + perror("ConnectToTcpAddr[ipv6]: socket"); + } else { + int dmsg = 0; + int res = connect(fd, ap->ai_addr, ap->ai_addrlen); +#if defined(SOL_IPV6) && defined(IPV6_V6ONLY) + if (res != 0) { + int zero = 0; + perror("ConnectToTcpAddr[ipv6]: connect"); + dmsg = 1; + if (setsockopt(fd, SOL_IPV6, IPV6_V6ONLY, (char *)&zero, sizeof(zero)) == 0) { + fprintf(stderr, "ConnectToTcpAddr[ipv6]: trying again with IPV6_V6ONLY=0\n"); + res = connect(fd, ap->ai_addr, ap->ai_addrlen); + dmsg = 0; + } + } +#endif + if (res == 0) { + fprintf(stderr, "ConnectToTcpAddr[ipv6]: connect OK\n"); + sock = fd; + break; + } else { + if (!dmsg) perror("ConnectToTcpAddr[ipv6]: connect"); + close(fd); + } + } + ap = ap->ai_next; + } + freeaddrinfo(ai); + } + if (sock >= 0 && setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { + perror("ConnectToTcpAddr: setsockopt"); + close(sock); + sock = -1; + } + } +#endif + return sock; } +Bool SocketPair(int fd[2]) { + if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd) == -1) { + perror("socketpair"); + return False; + } + return True; +} +Bool SetNoDelay(int sock) { + const int one = 1; + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { + perror("setsockopt"); + return False; + } + return True; +} /* * FindFreeTcpPort tries to find unused TCP port in the range @@ -242,29 +869,31 @@ int FindFreeTcpPort(void) { - int sock, port; - struct sockaddr_in addr; + int sock, port; + struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; + memset(&addr, 0, sizeof(struct sockaddr_in)); - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) { - fprintf(stderr,programName); - perror(": FindFreeTcpPort: socket"); - return 0; - } + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; - for (port = TUNNEL_PORT_OFFSET + 99; port > TUNNEL_PORT_OFFSET; port--) { - addr.sin_port = htons((unsigned short)port); - if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) { - close(sock); - return port; - } - } + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + fprintf(stderr,programName); + perror(": FindFreeTcpPort: socket"); + return 0; + } + + for (port = TUNNEL_PORT_OFFSET + 99; port > TUNNEL_PORT_OFFSET; port--) { + addr.sin_port = htons((unsigned short)port); + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) { + close(sock); + return port; + } + } - close(sock); - return 0; + close(sock); + return 0; } @@ -272,47 +901,110 @@ * ListenAtTcpPort starts listening at the given TCP port. */ -int -ListenAtTcpPort(int port) -{ - int sock; - struct sockaddr_in addr; - int one = 1; - - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = INADDR_ANY; +int use_loopback = 0; - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) { - fprintf(stderr,programName); - perror(": ListenAtTcpPort: socket"); - return -1; - } +int ListenAtTcpPort(int port) { + int sock; + struct sockaddr_in addr; + int one = 1; + + if (appData.noipv4) { + fprintf(stderr, "ipv4 is disabled via VNCVIEWER_NO_IPV4/-noipv4.\n"); + return -1; + } - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - (const char *)&one, sizeof(one)) < 0) { - fprintf(stderr,programName); - perror(": ListenAtTcpPort: setsockopt"); - close(sock); - return -1; - } + memset(&addr, 0, sizeof(struct sockaddr_in)); - if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - fprintf(stderr,programName); - perror(": ListenAtTcpPort: bind"); - close(sock); - return -1; - } + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; - if (listen(sock, 5) < 0) { - fprintf(stderr,programName); - perror(": ListenAtTcpPort: listen"); - close(sock); - return -1; - } + if (getenv("VNCVIEWER_LISTEN_LOCALHOST") || use_loopback) { + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + } + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror("ListenAtTcpPort: socket"); + return -1; + } - return sock; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, sizeof(one)) < 0) { + perror("ListenAtTcpPort: setsockopt"); + close(sock); + return -1; + } + + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("ListenAtTcpPort: bind"); + close(sock); + return -1; + } + + if (listen(sock, 32) < 0) { + perror("ListenAtTcpPort: listen"); + close(sock); + return -1; + } + + return sock; +} + +int ListenAtTcpPort6(int port) { + int sock = -1; +#ifdef AF_INET6 + struct sockaddr_in6 sin; + int one = 1; + + if (appData.noipv6) { + fprintf(stderr, "ipv6 is disabled via VNCVIEWER_NO_IPV6/-noipv6.\n"); + return -1; + } + + sock = socket(AF_INET6, SOCK_STREAM, 0); + if (sock < 0) { + perror("ListenAtTcpPort[ipv6]: socket"); + return -1; + } + + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) < 0) { + perror("ListenAtTcpPort[ipv6]: setsockopt1"); + close(sock); + return -1; + } + +#if defined(SOL_IPV6) && defined(IPV6_V6ONLY) + if (setsockopt(sock, SOL_IPV6, IPV6_V6ONLY, (char *)&one, sizeof(one)) < 0) { + perror("ListenAtTcpPort[ipv6]: setsockopt2"); + close(sock); + return -1; + } +#endif + + memset((char *)&sin, 0, sizeof(sin)); + sin.sin6_family = AF_INET6; + sin.sin6_port = htons(port); + sin.sin6_addr = in6addr_any; + + if (getenv("VNCVIEWER_LISTEN_LOCALHOST") || use_loopback) { + sin.sin6_addr = in6addr_loopback; + } + + if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { + perror("ListenAtTcpPort[ipv6]: bind"); + close(sock); + return -1; + } + + if (listen(sock, 32) < 0) { + perror("ListenAtTcpPort[ipv6]: listen"); + close(sock); + return -1; + } + +#endif + if (port) {} + return sock; } @@ -320,33 +1012,69 @@ * AcceptTcpConnection accepts a TCP connection. */ -int -AcceptTcpConnection(int listenSock) -{ - int sock; - struct sockaddr_in addr; - int addrlen = sizeof(addr); - int one = 1; +int AcceptTcpConnection(int listenSock) { + int sock; + struct sockaddr_in addr; + int addrlen = sizeof(addr); + int one = 1; + + sock = accept(listenSock, (struct sockaddr *) &addr, &addrlen); + if (sock < 0) { + perror("AcceptTcpConnection: accept"); + return -1; + } - sock = accept(listenSock, (struct sockaddr *) &addr, &addrlen); - if (sock < 0) { - fprintf(stderr,programName); - perror(": AcceptTcpConnection: accept"); - return -1; - } + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { + perror("AcceptTcpConnection: setsockopt"); + close(sock); + return -1; + } - if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, - (char *)&one, sizeof(one)) < 0) { - fprintf(stderr,programName); - perror(": AcceptTcpConnection: setsockopt"); - close(sock); - return -1; - } + return sock; +} + +char *accept6_ipaddr = NULL; +char *accept6_hostname = NULL; + +int AcceptTcpConnection6(int listenSock) { + int sock = -1; +#ifdef AF_INET6 + struct sockaddr_in6 addr; + socklen_t addrlen = sizeof(addr); + int one = 1; + char *name; + + if (appData.noipv6) { + return -1; + } + + sock = accept(listenSock, (struct sockaddr *) &addr, &addrlen); + if (sock < 0) { + perror("AcceptTcpConnection[ipv6]: accept"); + return -1; + } + + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { + perror("AcceptTcpConnection[ipv6]: setsockopt"); + close(sock); + return -1; + } - return sock; + name = ipv6_getipaddr((struct sockaddr *) &addr, addrlen); + if (!name) name = strdup("unknown"); + accept6_ipaddr = name; + fprintf(stderr, "AcceptTcpConnection6: ipv6 connection from: '%s'\n", name); + + name = ipv6_getnameinfo((struct sockaddr *) &addr, addrlen); + if (!name) name = strdup("unknown"); + accept6_hostname = name; +#endif + if (listenSock) {} + return sock; } + /* * SetNonBlocking sets a socket into non-blocking mode. */ @@ -379,7 +1107,7 @@ *addr = inet_addr(str); - if (*addr != -1) + if (*addr != (unsigned int) -1) return True; hp = gethostbyname(str); @@ -392,6 +1120,42 @@ return False; } +char *get_peer_ip(int sock) { + struct sockaddr_in saddr; + unsigned int saddr_len; + int saddr_port; + char *saddr_ip_str = NULL; + + saddr_len = sizeof(saddr); + memset(&saddr, 0, sizeof(saddr)); + saddr_port = -1; + if (!getpeername(sock, (struct sockaddr *)&saddr, &saddr_len)) { + saddr_ip_str = inet_ntoa(saddr.sin_addr); + } + if (! saddr_ip_str) { + saddr_ip_str = "unknown"; + } + return strdup(saddr_ip_str); +} + +char *ip2host(char *ip) { + char *str; + struct hostent *hp; + in_addr_t iaddr; + + iaddr = inet_addr(ip); + if (iaddr == htonl(INADDR_NONE)) { + return strdup("unknown"); + } + + hp = gethostbyaddr((char *)&iaddr, sizeof(in_addr_t), AF_INET); + if (!hp) { + return strdup("unknown"); + } + str = strdup(hp->h_name); + return str; +} + /* * Test if the other end of a socket is on the same machine. diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/tight.c vnc_unixsrc/vncviewer/tight.c --- vnc_unixsrc.orig/vncviewer/tight.c 2002-04-30 09:07:31.000000000 -0400 +++ vnc_unixsrc/vncviewer/tight.c 2008-10-05 15:16:35.000000000 -0400 @@ -129,14 +129,21 @@ #endif #if (BPP == 8) - gcv.foreground = (appData.useBGR233) ? - BGR233ToPixel[fill_colour] : fill_colour; + gcv.foreground = (appData.useBGR233) ? BGR233ToPixel[fill_colour] : fill_colour; +#else +#if (BPP == 16) + gcv.foreground = (appData.useBGR565) ? BGR565ToPixel[fill_colour] : fill_colour; #else gcv.foreground = fill_colour; #endif +#endif - XChangeGC(dpy, gc, GCForeground, &gcv); - XFillRectangle(dpy, desktopWin, gc, rx, ry, rw, rh); + if (!appData.useXserverBackingStore) { + FillScreen(rx, ry, rw, rh, gcv.foreground); + } else { + XChangeGC(dpy, gc, GCForeground, &gcv); + XFillRectangle(dpy, desktopWin, gc, rx, ry, rw, rh); + } return True; } diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/tmake vnc_unixsrc/vncviewer/tmake --- vnc_unixsrc.orig/vncviewer/tmake 1969-12-31 19:00:00.000000000 -0500 +++ vnc_unixsrc/vncviewer/tmake 2009-10-25 10:31:22.000000000 -0400 @@ -0,0 +1,17 @@ +#!/bin/sh +TURBOVNC_DIR=/home/runge/turbojpeg +make clean +(cd ../libvncauth || exit 1; make) +if [ "X$1" = "X-a" ]; then + exit +fi +make CCOPTIONS=-DTURBOVNC EXTRA_LIBRARIES="-L$TURBOVNC_DIR -Xlinker --rpath=$TURBOVNC_DIR -Xlinker --rpath=/usr/local/lib -lturbojpeg" +cp -p vncviewer vncviewer.turbovnc +strip vncviewer.turbovnc +ls -l vncviewer.turbovnc +ldd vncviewer.turbovnc + +echo +make clean all +ls -l vncviewer +ldd vncviewer diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/tunnel.c vnc_unixsrc/vncviewer/tunnel.c --- vnc_unixsrc.orig/vncviewer/tunnel.c 2003-07-31 04:03:49.000000000 -0400 +++ vnc_unixsrc/vncviewer/tunnel.c 2010-02-25 23:39:24.000000000 -0500 @@ -100,7 +100,6 @@ int *pargc, char **argv, int tunnelArgIndex) { char *pdisplay; - int port; if (tunnelArgIndex >= *pargc - 1) usage(); @@ -132,6 +131,7 @@ { char *colonPos; int len, portOffset; + int disp; if (tunnelArgIndex >= *pargc - 2) usage(); @@ -150,10 +150,17 @@ len--; portOffset = 0; } - if (!len || strspn(colonPos, "-0123456789") != len) { + if (!len || (int) strspn(colonPos, "-0123456789") != len) { usage(); } +#if 0 *remotePort = atoi(colonPos) + portOffset; +#else + disp = atoi(colonPos); + if (portOffset != 0 && disp >= 100) + portOffset = 0; + *remotePort = disp + portOffset; +#endif } sprintf(lastArgv, "localhost::%d", localPort); diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/turbovnc/tight.c vnc_unixsrc/vncviewer/turbovnc/tight.c --- vnc_unixsrc.orig/vncviewer/turbovnc/tight.c 1969-12-31 19:00:00.000000000 -0500 +++ vnc_unixsrc/vncviewer/turbovnc/tight.c 2008-08-20 13:35:58.000000000 -0400 @@ -0,0 +1,613 @@ +/* + * Copyright (C) 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (C) 2004 Landmark Graphics Corporation. All Rights Reserved. + * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * tight.c - handle ``tight'' encoding. + * + * This file shouldn't be compiled directly. It is included multiple + * times by rfbproto.c, each time with a different definition of the + * macro BPP. For each value of BPP, this file defines a function + * which handles a tight-encoded rectangle with BPP bits per pixel. + * + */ + +#define TIGHT_MIN_TO_COMPRESS 12 + +#define CARDBPP CONCAT2E(CARD,BPP) +#define filterPtrBPP CONCAT2E(filterPtr,BPP) + +#define HandleTightBPP CONCAT2E(HandleTight,BPP) +#define InitFilterCopyBPP CONCAT2E(InitFilterCopy,BPP) +#define InitFilterPaletteBPP CONCAT2E(InitFilterPalette,BPP) +#define InitFilterGradientBPP CONCAT2E(InitFilterGradient,BPP) +#define FilterCopyBPP CONCAT2E(FilterCopy,BPP) +#define FilterPaletteBPP CONCAT2E(FilterPalette,BPP) +#define FilterGradientBPP CONCAT2E(FilterGradient,BPP) + +#if BPP != 8 +#define DecompressJpegRectBPP CONCAT2E(DecompressJpegRect,BPP) +#endif + +#ifndef RGB_TO_PIXEL + +#define RGB_TO_PIXEL(bpp,r,g,b) \ + (((CARD##bpp)(r) & myFormat.redMax) << myFormat.redShift | \ + ((CARD##bpp)(g) & myFormat.greenMax) << myFormat.greenShift | \ + ((CARD##bpp)(b) & myFormat.blueMax) << myFormat.blueShift) + +#define RGB24_TO_PIXEL(bpp,r,g,b) \ + ((((CARD##bpp)(r) & 0xFF) * myFormat.redMax + 127) / 255 \ + << myFormat.redShift | \ + (((CARD##bpp)(g) & 0xFF) * myFormat.greenMax + 127) / 255 \ + << myFormat.greenShift | \ + (((CARD##bpp)(b) & 0xFF) * myFormat.blueMax + 127) / 255 \ + << myFormat.blueShift) + +#define RGB24_TO_PIXEL32(r,g,b) \ + (((CARD32)(r) & 0xFF) << myFormat.redShift | \ + ((CARD32)(g) & 0xFF) << myFormat.greenShift | \ + ((CARD32)(b) & 0xFF) << myFormat.blueShift) + +#endif + +extern XImage *image; + +/* Type declarations */ + +typedef void (*filterPtrBPP)(int, int, int); + +/* Prototypes */ + +static int InitFilterCopyBPP (int rw, int rh); +static int InitFilterPaletteBPP (int rw, int rh); +static int InitFilterGradientBPP (int rw, int rh); +static void FilterCopyBPP (int srcx, int srcy, int numRows); +static void FilterPaletteBPP (int srcx, int srcy, int numRows); +static void FilterGradientBPP (int srcx, int srcy, int numRows); + +static Bool DecompressJpegRectBPP(int x, int y, int w, int h); + +/* Definitions */ + +static Bool +HandleTightBPP (int rx, int ry, int rw, int rh) +{ + CARDBPP fill_colour; + XGCValues gcv; + CARD8 comp_ctl; + CARD8 filter_id; + filterPtrBPP filterFn; + z_streamp zs; + int err, stream_id, compressedLen, bitsPixel; + int bufferSize, rowSize, numRows; + Bool readUncompressed = False; + CARDBPP *rawData; + + if (!ReadFromRFBServer((char *)&comp_ctl, 1)) + return False; + + /* Flush zlib streams if we are told by the server to do so. */ + for (stream_id = 0; stream_id < 4; stream_id++) { + if ((comp_ctl & 1) && zlibStreamActive[stream_id]) { + if (inflateEnd (&zlibStream[stream_id]) != Z_OK && + zlibStream[stream_id].msg != NULL) + fprintf(stderr, "inflateEnd: %s\n", zlibStream[stream_id].msg); + zlibStreamActive[stream_id] = False; + } + comp_ctl >>= 1; + } + + if ((comp_ctl & rfbTightNoZlib) == rfbTightNoZlib) { + comp_ctl &= ~(rfbTightNoZlib); + readUncompressed = True; + } + + /* Handle solid rectangles. */ + if (comp_ctl == rfbTightFill) { +#if BPP == 32 + if (myFormat.depth == 24 && myFormat.redMax == 0xFF && + myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) { + if (!ReadFromRFBServer(buffer, 3)) + return False; + fill_colour = RGB24_TO_PIXEL32(buffer[0], buffer[1], buffer[2]); + } else { + if (!ReadFromRFBServer((char*)&fill_colour, sizeof(fill_colour))) + return False; + } +#else + if (!ReadFromRFBServer((char*)&fill_colour, sizeof(fill_colour))) + return False; +#endif + +#if (BPP == 8) + gcv.foreground = (appData.useBGR233) ? + BGR233ToPixel[fill_colour] : fill_colour; +#else + gcv.foreground = fill_colour; +#endif + + FillRectangle(&gcv, rx, ry, rw, rh); + return True; + } + +#if BPP == 8 + if (comp_ctl == rfbTightJpeg) { + fprintf(stderr, "Tight encoding: JPEG is not supported in 8 bpp mode.\n"); + return False; + } +#else + if (comp_ctl == rfbTightJpeg) { + return DecompressJpegRectBPP(rx, ry, rw, rh); + } +#endif + + /* Quit on unsupported subencoding value. */ + if (comp_ctl > rfbTightMaxSubencoding) { + fprintf(stderr, "Tight encoding: bad subencoding value received.\n"); + return False; + } + + /* + * Here primary compression mode handling begins. + * Data was processed with optional filter + zlib compression. + */ + + /* First, we should identify a filter to use. */ + if ((comp_ctl & rfbTightExplicitFilter) != 0) { + if (!ReadFromRFBServer((char*)&filter_id, 1)) + return False; + + switch (filter_id) { + case rfbTightFilterCopy: + filterFn = FilterCopyBPP; + bitsPixel = InitFilterCopyBPP(rw, rh); + break; + case rfbTightFilterPalette: + filterFn = FilterPaletteBPP; + bitsPixel = InitFilterPaletteBPP(rw, rh); + break; + case rfbTightFilterGradient: + filterFn = FilterGradientBPP; + bitsPixel = InitFilterGradientBPP(rw, rh); + break; + default: + fprintf(stderr, "Tight encoding: unknown filter code received.\n"); + return False; + } + } else { + filterFn = FilterCopyBPP; + bitsPixel = InitFilterCopyBPP(rw, rh); + } + if (bitsPixel == 0) { + fprintf(stderr, "Tight encoding: error receiving palette.\n"); + return False; + } + + /* Determine if the data should be decompressed or just copied. */ + rowSize = (rw * bitsPixel + 7) / 8; + bufferSize = -1; + if (rh * rowSize < TIGHT_MIN_TO_COMPRESS) + bufferSize = rh * rowSize; + else if (readUncompressed) { + bufferSize = (int)ReadCompactLen(); + } + if (bufferSize != -1) { + uncompressedData = (char *)realloc(uncompressedData, bufferSize); + if (!uncompressedData) { + fprintf(stderr, "Memory allocation error\n"); + return False; + } + if (!ReadFromRFBServer(uncompressedData, bufferSize)) + return False; + filterFn(rx, ry, rh); + if (appData.useBGR233) CopyDataToImage(buffer, rx, ry, rw, rh); + if (!appData.doubleBuffer) CopyImageToScreen(rx, ry, rw, rh); + + return True; + } + + /* Read the length (1..3 bytes) of compressed data following. */ + compressedLen = (int)ReadCompactLen(); + if (compressedLen <= 0) { + fprintf(stderr, "Incorrect data received from the server.\n"); + return False; + } + + /* Now let's initialize compression stream if needed. */ + stream_id = comp_ctl & 0x03; + zs = &zlibStream[stream_id]; + if (!zlibStreamActive[stream_id]) { + zs->zalloc = Z_NULL; + zs->zfree = Z_NULL; + zs->opaque = Z_NULL; + err = inflateInit(zs); + if (err != Z_OK) { + if (zs->msg != NULL) + fprintf(stderr, "InflateInit error: %s.\n", zs->msg); + return False; + } + zlibStreamActive[stream_id] = True; + } + + /* Read, decode and draw actual pixel data in a loop. */ + + compressedData = (char *)realloc(compressedData, compressedLen); + if (!compressedData) { + fprintf(stderr, "Memory allocation error\n"); + return False; + } + uncompressedData = (char *)realloc(uncompressedData, rh * rowSize); + if (!uncompressedData) { + fprintf(stderr, "Memory allocation error\n"); + return False; + } + + if (!ReadFromRFBServer(compressedData, compressedLen)) + return False; + zs->next_in = (Bytef *)compressedData; + zs->avail_in = compressedLen; + zs->next_out = (Bytef *)uncompressedData; + zs->avail_out = rh * rowSize; + + err = inflate(zs, Z_SYNC_FLUSH); + if (err != Z_OK && err != Z_STREAM_END) { + if (zs->msg != NULL) { + fprintf(stderr, "Inflate error: %s.\n", zs->msg); + } else { + fprintf(stderr, "Inflate error: %d.\n", err); + } + return False; + } + + filterFn(rx, ry, rh); + if (appData.useBGR233) CopyDataToImage(buffer, rx, ry, rw, rh); + if (!appData.doubleBuffer) CopyImageToScreen(rx, ry, rw, rh); + + return True; +} + +/*---------------------------------------------------------------------------- + * + * Filter stuff. + * + */ + +/* + The following variables are defined in rfbproto.c: + static Bool cutZeros; + static int rectWidth, rectColors; + static CARD8 tightPalette[256*4]; + static CARD8 tightPrevRow[2048*3*sizeof(CARD16)]; +*/ + +static int +InitFilterCopyBPP (int rw, int rh) +{ + rectWidth = rw; + +#if BPP == 32 + if (myFormat.depth == 24 && myFormat.redMax == 0xFF && + myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) { + cutZeros = True; + return 24; + } else { + cutZeros = False; + } +#endif + + return BPP; +} + +static void +FilterCopyBPP (int srcx, int srcy, int numRows) +{ + CARDBPP *dst = (CARDBPP *)&image->data[srcy * image->bytes_per_line + + srcx * image->bits_per_pixel/8]; + int dstw = image->bytes_per_line / (image->bits_per_pixel / 8); + int y; +#if BPP == 32 + int x; +#endif + + if (appData.useBGR233) { + dst = (CARDBPP *)buffer; + dstw = rectWidth; + } + +#if BPP == 32 + if (cutZeros) { + for (y = 0; y < numRows; y++) { + for (x = 0; x < rectWidth; x++) { + dst[y*dstw+x] = + RGB24_TO_PIXEL32(uncompressedData[(y*rectWidth+x)*3], + uncompressedData[(y*rectWidth+x)*3+1], + uncompressedData[(y*rectWidth+x)*3+2]); + } + } + return; + } +#endif + + for (y = 0; y < numRows; y++) + memcpy (&dst[y*dstw], &uncompressedData[y*rectWidth], rectWidth * (BPP / 8)); +} + +static int +InitFilterGradientBPP (int rw, int rh) +{ + int bits; + + bits = InitFilterCopyBPP(rw, rh); + if (cutZeros) + memset(tightPrevRow, 0, rw * 3); + else + memset(tightPrevRow, 0, rw * 3 * sizeof(CARD16)); + + return bits; +} + +#if BPP == 32 + +static void +FilterGradient24 (int srcx, int srcy, int numRows) +{ + CARDBPP *dst = (CARDBPP *)&image->data[srcy * image->bytes_per_line + + srcx * image->bits_per_pixel/8]; + int dstw = image->bytes_per_line / (image->bits_per_pixel / 8); + int x, y, c; + CARD8 thisRow[2048*3]; + CARD8 pix[3]; + int est[3]; + + if (appData.useBGR233) { + dst = (CARDBPP *)buffer; + dstw = rectWidth; + } + + for (y = 0; y < numRows; y++) { + + /* First pixel in a row */ + for (c = 0; c < 3; c++) { + pix[c] = tightPrevRow[c] + uncompressedData[y*rectWidth*3+c]; + thisRow[c] = pix[c]; + } + dst[y*dstw] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); + + /* Remaining pixels of a row */ + for (x = 1; x < rectWidth; x++) { + for (c = 0; c < 3; c++) { + est[c] = (int)tightPrevRow[x*3+c] + (int)pix[c] - + (int)tightPrevRow[(x-1)*3+c]; + if (est[c] > 0xFF) { + est[c] = 0xFF; + } else if (est[c] < 0x00) { + est[c] = 0x00; + } + pix[c] = (CARD8)est[c] + buffer[(y*rectWidth+x)*3+c]; + thisRow[x*3+c] = pix[c]; + } + dst[y*dstw+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); + } + + memcpy(tightPrevRow, thisRow, rectWidth * 3); + } +} + +#endif + +static void +FilterGradientBPP (int srcx, int srcy, int numRows) +{ + int x, y, c; + CARDBPP *dst = (CARDBPP *)&image->data[srcy * image->bytes_per_line + + srcx * image->bits_per_pixel/8]; + int dstw = image->bytes_per_line / (image->bits_per_pixel / 8); + CARDBPP *src = (CARDBPP *)uncompressedData; + CARD16 *thatRow = (CARD16 *)tightPrevRow; + CARD16 thisRow[2048*3]; + CARD16 pix[3]; + CARD16 max[3]; + int shift[3]; + int est[3]; + + if (appData.useBGR233) { + dst = (CARDBPP *)buffer; + dstw = rectWidth; + } + +#if BPP == 32 + if (cutZeros) { + FilterGradient24(srcx, srcy, numRows); + return; + } +#endif + + max[0] = myFormat.redMax; + max[1] = myFormat.greenMax; + max[2] = myFormat.blueMax; + + shift[0] = myFormat.redShift; + shift[1] = myFormat.greenShift; + shift[2] = myFormat.blueShift; + + for (y = 0; y < numRows; y++) { + + /* First pixel in a row */ + for (c = 0; c < 3; c++) { + pix[c] = (CARD16)((src[y*rectWidth] >> shift[c]) + thatRow[c] & max[c]); + thisRow[c] = pix[c]; + } + dst[y*dstw] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]); + + /* Remaining pixels of a row */ + for (x = 1; x < rectWidth; x++) { + for (c = 0; c < 3; c++) { + est[c] = (int)thatRow[x*3+c] + (int)pix[c] - (int)thatRow[(x-1)*3+c]; + if (est[c] > (int)max[c]) { + est[c] = (int)max[c]; + } else if (est[c] < 0) { + est[c] = 0; + } + pix[c] = (CARD16)((src[y*rectWidth+x] >> shift[c]) + est[c] & max[c]); + thisRow[x*3+c] = pix[c]; + } + dst[y*dstw+x] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]); + } + memcpy(thatRow, thisRow, rectWidth * 3 * sizeof(CARD16)); + } +} + +static int +InitFilterPaletteBPP (int rw, int rh) +{ + int i; + CARD8 numColors; + CARDBPP *palette = (CARDBPP *)tightPalette; + + rectWidth = rw; + + if (!ReadFromRFBServer((char*)&numColors, 1)) + return 0; + + rectColors = (int)numColors; + if (++rectColors < 2) + return 0; + +#if BPP == 32 + if (myFormat.depth == 24 && myFormat.redMax == 0xFF && + myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) { + if (!ReadFromRFBServer((char*)&tightPalette, rectColors * 3)) + return 0; + for (i = rectColors - 1; i >= 0; i--) { + palette[i] = RGB24_TO_PIXEL32(tightPalette[i*3], + tightPalette[i*3+1], + tightPalette[i*3+2]); + } + return (rectColors == 2) ? 1 : 8; + } +#endif + + if (!ReadFromRFBServer((char*)&tightPalette, rectColors * (BPP / 8))) + return 0; + + return (rectColors == 2) ? 1 : 8; +} + +static void +FilterPaletteBPP (int srcx, int srcy, int numRows) +{ + int x, y, b, w; + CARDBPP *dst = (CARDBPP *)&image->data[srcy * image->bytes_per_line + + srcx * image->bits_per_pixel/8]; + int dstw = image->bytes_per_line / (image->bits_per_pixel / 8); + CARD8 *src = (CARD8 *)uncompressedData; + CARDBPP *palette = (CARDBPP *)tightPalette; + + if (appData.useBGR233) { + dst = (CARDBPP *)buffer; + dstw = rectWidth; + } + + if (rectColors == 2) { + w = (rectWidth + 7) / 8; + for (y = 0; y < numRows; y++) { + for (x = 0; x < rectWidth / 8; x++) { + for (b = 7; b >= 0; b--) + dst[y*dstw+x*8+7-b] = palette[src[y*w+x] >> b & 1]; + } + for (b = 7; b >= 8 - rectWidth % 8; b--) { + dst[y*dstw+x*8+7-b] = palette[src[y*w+x] >> b & 1]; + } + } + } else { + for (y = 0; y < numRows; y++) + for (x = 0; x < rectWidth; x++) + dst[y*dstw+x] = palette[(int)src[y*rectWidth+x]]; + } +} + +#if BPP != 8 + +/*---------------------------------------------------------------------------- + * + * JPEG decompression. + * + */ + +/* + The following variables are defined in rfbproto.c: + static Bool jpegError; + static struct jpeg_source_mgr jpegSrcManager; + static JOCTET *jpegBufferPtr; + static size_t *jpegBufferLen; +*/ + +static Bool +DecompressJpegRectBPP(int x, int y, int w, int h) +{ + int compressedLen; + char *dstptr; + int ps, flags=0; + + compressedLen = (int)ReadCompactLen(); + if (compressedLen <= 0) { + fprintf(stderr, "Incorrect data received from the server.\n"); + return False; + } + + compressedData = (char *)realloc(compressedData, compressedLen); + if (compressedData == NULL) { + fprintf(stderr, "Memory allocation error.\n"); + return False; + } + + if (!ReadFromRFBServer(compressedData, compressedLen)) { + return False; + } + + if(!tjhnd) { + if((tjhnd=tjInitDecompress())==NULL) { + fprintf(stderr, "TurboJPEG error: %s\n", tjGetErrorStr()); + return False; + } + } + + ps=image->bits_per_pixel/8; + if(myFormat.bigEndian && ps==4) flags|=TJ_ALPHAFIRST; + if(myFormat.redShift==16 && myFormat.blueShift==0) + flags|=TJ_BGR; + if(myFormat.bigEndian) flags^=TJ_BGR; + + dstptr=&image->data[image->bytes_per_line*y+x*ps]; + if(tjDecompress(tjhnd, (unsigned char *)compressedData, (unsigned long)compressedLen, + (unsigned char *)dstptr, w, image->bytes_per_line, h, ps, flags)==-1) { + fprintf(stderr, "TurboJPEG error: %s\n", tjGetErrorStr()); + return False; + } + + if (!appData.doubleBuffer) + CopyImageToScreen(x, y, w, h); + + return True; +} + +#endif + diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/turbovnc/turbojpeg.h vnc_unixsrc/vncviewer/turbovnc/turbojpeg.h --- vnc_unixsrc.orig/vncviewer/turbovnc/turbojpeg.h 1969-12-31 19:00:00.000000000 -0500 +++ vnc_unixsrc/vncviewer/turbovnc/turbojpeg.h 2008-04-03 04:28:56.000000000 -0400 @@ -0,0 +1,229 @@ +/* Copyright (C)2004 Landmark Graphics + * Copyright (C)2005, 2006 Sun Microsystems, Inc. + * + * This library is free software and may be redistributed and/or modified under + * the terms of the wxWindows Library License, Version 3.1 or (at your option) + * any later version. The full license is in the LICENSE.txt file included + * with this distribution. + * + * This library 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 + * wxWindows Library License for more details. + */ + +#if (defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__)) && defined(_WIN32) && defined(DLLDEFINE) +#define DLLEXPORT __declspec(dllexport) +#else +#define DLLEXPORT +#endif + +#define DLLCALL + +/* Subsampling */ +#define NUMSUBOPT 4 + +enum {TJ_444=0, TJ_422, TJ_411, TJ_GRAYSCALE}; + +/* Flags */ +#define TJ_BGR 1 +#define TJ_BOTTOMUP 2 +#define TJ_FORCEMMX 8 /* Force IPP to use MMX code even if SSE available */ +#define TJ_FORCESSE 16 /* Force IPP to use SSE1 code even if SSE2 available */ +#define TJ_FORCESSE2 32 /* Force IPP to use SSE2 code (useful if auto-detect is not working properly) */ +#define TJ_ALPHAFIRST 64 /* BGR buffer is ABGR and RGB buffer is ARGB */ +#define TJ_FORCESSE3 128 /* Force IPP to use SSE3 code (useful if auto-detect is not working properly) */ + +typedef void* tjhandle; + +#define TJPAD(p) (((p)+3)&(~3)) +#ifndef max + #define max(a,b) ((a)>(b)?(a):(b)) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* API follows */ + + +/* + tjhandle tjInitCompress(void) + + Creates a new JPEG compressor instance, allocates memory for the structures, + and returns a handle to the instance. Most applications will only + need to call this once at the beginning of the program or once for each + concurrent thread. Don't try to create a new instance every time you + compress an image, because this will cause performance to suffer. + + RETURNS: NULL on error +*/ +DLLEXPORT tjhandle DLLCALL tjInitCompress(void); + + +/* + int tjCompress(tjhandle j, + unsigned char *srcbuf, int width, int pitch, int height, int pixelsize, + unsigned char *dstbuf, unsigned long *size, + int jpegsubsamp, int jpegqual, int flags) + + [INPUT] j = instance handle previously returned from a call to + tjInitCompress() + [INPUT] srcbuf = pointer to user-allocated image buffer containing pixels in + RGB(A) or BGR(A) form + [INPUT] width = width (in pixels) of the source image + [INPUT] pitch = bytes per line of the source image (width*pixelsize if the + bitmap is unpadded, else TJPAD(width*pixelsize) if each line of the bitmap + is padded to the nearest 32-bit boundary, such as is the case for Windows + bitmaps. You can also be clever and use this parameter to skip lines, etc., + as long as the pitch is greater than 0.) + [INPUT] height = height (in pixels) of the source image + [INPUT] pixelsize = size (in bytes) of each pixel in the source image + RGBA and BGRA: 4, RGB and BGR: 3 + [INPUT] dstbuf = pointer to user-allocated image buffer which will receive + the JPEG image. Use the macro TJBUFSIZE(width, height) to determine + the appropriate size for this buffer based on the image width and height. + [OUTPUT] size = pointer to unsigned long which receives the size (in bytes) + of the compressed image + [INPUT] jpegsubsamp = Specifies either 4:1:1, 4:2:2, or 4:4:4 subsampling. + When the image is converted from the RGB to YCbCr colorspace as part of the + JPEG compression process, every other Cb and Cr (chrominance) pixel can be + discarded to produce a smaller image with little perceptible loss of + image clarity (the human eye is more sensitive to small changes in + brightness than small changes in color.) + + TJ_411: 4:1:1 subsampling. Discards every other Cb, Cr pixel in both + horizontal and vertical directions. + TJ_422: 4:2:2 subsampling. Discards every other Cb, Cr pixel only in + the horizontal direction. + TJ_444: no subsampling. + TJ_GRAYSCALE: Generate grayscale JPEG image + + [INPUT] jpegqual = JPEG quality (an integer between 0 and 100 inclusive.) + [INPUT] flags = the bitwise OR of one or more of the following + + TJ_BGR: The components of each pixel in the source image are stored in + B,G,R order, not R,G,B + TJ_BOTTOMUP: The source image is stored in bottom-up (Windows) order, + not top-down + TJ_FORCEMMX: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use MMX code (bypass CPU auto-detection) + TJ_FORCESSE: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use SSE code (bypass CPU auto-detection) + TJ_FORCESSE2: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use SSE2 code (bypass CPU auto-detection) + TJ_FORCESSE3: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use SSE3 code (bypass CPU auto-detection) + + RETURNS: 0 on success, -1 on error +*/ +DLLEXPORT int DLLCALL tjCompress(tjhandle j, + unsigned char *srcbuf, int width, int pitch, int height, int pixelsize, + unsigned char *dstbuf, unsigned long *size, + int jpegsubsamp, int jpegqual, int flags); + +DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height); + +/* + tjhandle tjInitDecompress(void) + + Creates a new JPEG decompressor instance, allocates memory for the + structures, and returns a handle to the instance. Most applications will + only need to call this once at the beginning of the program or once for each + concurrent thread. Don't try to create a new instance every time you + decompress an image, because this will cause performance to suffer. + + RETURNS: NULL on error +*/ +DLLEXPORT tjhandle DLLCALL tjInitDecompress(void); + + +/* + int tjDecompressHeader(tjhandle j, + unsigned char *srcbuf, unsigned long size, + int *width, int *height) + + [INPUT] j = instance handle previously returned from a call to + tjInitDecompress() + [INPUT] srcbuf = pointer to a user-allocated buffer containing the JPEG image + to decompress + [INPUT] size = size of the JPEG image buffer (in bytes) + [OUTPUT] width = width (in pixels) of the JPEG image + [OUTPUT] height = height (in pixels) of the JPEG image + + RETURNS: 0 on success, -1 on error +*/ +DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle j, + unsigned char *srcbuf, unsigned long size, + int *width, int *height); + + +/* + int tjDecompress(tjhandle j, + unsigned char *srcbuf, unsigned long size, + unsigned char *dstbuf, int width, int pitch, int height, int pixelsize, + int flags) + + [INPUT] j = instance handle previously returned from a call to + tjInitDecompress() + [INPUT] srcbuf = pointer to a user-allocated buffer containing the JPEG image + to decompress + [INPUT] size = size of the JPEG image buffer (in bytes) + [INPUT] dstbuf = pointer to user-allocated image buffer which will receive + the bitmap image. This buffer should normally be pitch*height + bytes in size, although this pointer may also be used to decompress into + a specific region of a larger buffer. + [INPUT] width = width (in pixels) of the destination image + [INPUT] pitch = bytes per line of the destination image (width*pixelsize if the + bitmap is unpadded, else TJPAD(width*pixelsize) if each line of the bitmap + is padded to the nearest 32-bit boundary, such as is the case for Windows + bitmaps. You can also be clever and use this parameter to skip lines, etc., + as long as the pitch is greater than 0.) + [INPUT] height = height (in pixels) of the destination image + [INPUT] pixelsize = size (in bytes) of each pixel in the destination image + RGBA/RGBx and BGRA/BGRx: 4, RGB and BGR: 3 + [INPUT] flags = the bitwise OR of one or more of the following + + TJ_BGR: The components of each pixel in the destination image should be + written in B,G,R order, not R,G,B + TJ_BOTTOMUP: The destination image should be stored in bottom-up + (Windows) order, not top-down + TJ_FORCEMMX: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use MMX code (bypass CPU auto-detection) + TJ_FORCESSE: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use SSE code (bypass CPU auto-detection) + TJ_FORCESSE2: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use SSE2 code (bypass CPU auto-detection) + + RETURNS: 0 on success, -1 on error +*/ +DLLEXPORT int DLLCALL tjDecompress(tjhandle j, + unsigned char *srcbuf, unsigned long size, + unsigned char *dstbuf, int width, int pitch, int height, int pixelsize, + int flags); + + +/* + int tjDestroy(tjhandle h) + + Frees structures associated with a compression or decompression instance + + [INPUT] h = instance handle (returned from a previous call to + tjInitCompress() or tjInitDecompress() + + RETURNS: 0 on success, -1 on error +*/ +DLLEXPORT int DLLCALL tjDestroy(tjhandle h); + + +/* + char *tjGetErrorStr(void) + + Returns a descriptive error message explaining why the last command failed +*/ +DLLEXPORT char* DLLCALL tjGetErrorStr(void); + +#ifdef __cplusplus +} +#endif diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/vncviewer._man vnc_unixsrc/vncviewer/vncviewer._man --- vnc_unixsrc.orig/vncviewer/vncviewer._man 1969-12-31 19:00:00.000000000 -0500 +++ vnc_unixsrc/vncviewer/vncviewer._man 2010-04-11 23:30:24.000000000 -0400 @@ -0,0 +1,829 @@ +'\" t +.\" ** The above line should force tbl to be a preprocessor ** +.\" Man page for X vncviewer +.\" +.\" Copyright (C) 1998 Marcus.Brinkmann@ruhr-uni-bochum.de +.\" Copyright (C) 2000,2001 Red Hat, Inc. +.\" Copyright (C) 2001-2003 Constantin Kaplinsky <const@ce.cctpu.edu.ru> +.\" Copyright (C) 2006-2010 Karl J. Runge <runge@karlrunge.com> +.\" +.\" You may distribute under the terms of the GNU General Public +.\" License as specified in the file LICENCE.TXT that comes with the +.\" TightVNC distribution. +.\" +.TH ssvncviewer 1 "April 2010" "" "SSVNC" +.SH NAME +ssvncviewer \- an X viewer client for VNC +.SH SYNOPSIS +.B ssvncviewer +.RI [\| options \|] +.RI [\| host \|][\| :display \|] +.br +.B ssvncviewer +.RI [\| options \|] +.RI [\| host \|][\| ::port \|] +.br +.B ssvncviewer +.RI [\| options \|] +.RI exec=[\| cmd+args... \|] +.br +.B ssvncviewer +.RI [\| options \|] +.RI fd=n +.br +.B ssvncviewer +.RI [\| options \|] +.RI /path/to/unix/socket +.br +.B ssvncviewer +.RI [\| options \|] +.IR \-listen +.RI [\| display \|] +.br +.B ssvncviewer +.IR \-help +.br +.SH DESCRIPTION +.B ssvncviewer +is an Xt\-based client application for the VNC (Virtual Network +Computing) system. It can connect to any VNC\-compatible server such +as \fBXvnc\fR, WinVNC, or \fBx11vnc\fR, allowing you to control desktop environment +of a different machine. + +ssvncviewer is an enhanced version of the tightvnc unix viewer that can +take advantage of features in the \fBx11vnc\fR and UltraVNC VNC servers. +See below for the description of these features. + +You can use F8 to display a pop\-up utility menu. Press F8 twice to +pass single F8 to the remote side. +.SH OPTIONS +.TP +\fB\-help\fR +Prints a short usage notice to stderr. +.TP +\fB\-listen\fR +Make the viewer listen on port 5500+\fIdisplay\fR for reverse +connections from a server. WinVNC supports reverse connections using +the "Add New Client" menu option, or the \-connect command line +option. \fBXvnc\fR requires the use of the helper program +\fBvncconnect\fR. +.TP +\fB\-via\fR \fIgateway\fR +Automatically create encrypted TCP tunnel to the \fIgateway\fR machine +before connection, connect to the \fIhost\fR through that tunnel +(TightVNC\-specific). By default, this option invokes SSH local port +forwarding, assuming that SSH client binary can be accessed as +/usr/bin/ssh. Note that when using the \fB\-via\fR option, the host +machine name should be specified as known to the gateway machine, e.g. +"localhost" denotes the \fIgateway\fR, not the machine where vncviewer +was launched. See the ENVIRONMENT section below for the information on +configuring the \fB\-via\fR option. +.TP +\fB\-shared\fR +When connecting, specify that a shared connection is requested. In +TightVNC, this is the default mode, allowing you to share the desktop +with other clients already using it. +.TP +\fB\-noshared\fR +When connecting, specify that the session may not be shared. This +would either disconnect other connected clients or refuse your +connection, depending on the server configuration. +.TP +\fB\-viewonly\fR +Disable transfer of mouse and keyboard events from the client to the +server. +.TP +\fB\-fullscreen\fR +Start in full\-screen mode. Please be aware that operating in +full\-screen mode may confuse X window managers. Typically, such +conflicts cause incorrect handling of input focus or make the viewer +window disappear mysteriously. See the grabKeyboard setting in the +RESOURCES section below for a method to solve input focus problem. +.TP +\fB\-noraiseonbeep\fR +By default, the viewer shows and raises its window on remote beep +(bell) event. This option disables such behaviour +(TightVNC\-specific). +.TP +\fB\-user\fR \fIusername\fR +User name for Unix login authentication. Default is to use current +Unix user name. If this option was given, the viewer will prefer Unix +login authentication over the standard VNC authentication. +.TP +\fB\-passwd\fR \fIpasswd\-file\fR +File from which to get the password (as generated by the +\fBvncpasswd\fR(1) program). This option affects only the standard VNC +authentication. +.TP +\fB\-encodings\fR \fIencoding\-list\fR +TightVNC supports several different compression methods to encode +screen updates; this option specifies a set of them to use in order of +preference. Encodings are specified separated with spaces, and must +thus be enclosed in quotes if more than one is specified. Commas may be used to avoid spaces. +Available encodings, in default order for a remote connection, are +"copyrect tight hextile zlib corre rre raw". For a local connection +(to the same machine), the default order to try is "raw copyrect tight +hextile zlib corre rre". Raw encoding is always assumed as a last option +if no other encoding can be used for some reason. For more information +on encodings, see the section ENCODINGS below. +.TP +\fB\-bgr233\fR +Always use the BGR233 format to encode pixel data. This reduces +network traffic, but colors may be represented inaccurately. The +bgr233 format is an 8\-bit "true color" format, with 2 bits blue, 3 +bits green, and 3 bits red. +.TP +\fB\-owncmap\fR +Try to use a PseudoColor visual and a private colormap. This allows +the VNC server to control the colormap. +.TP +\fB\-truecolour\fR, \fB\-truecolor\fR +Try to use a TrueColor visual. +.TP +\fB\-depth\fR \fIdepth\fR +On an X server which supports multiple TrueColor visuals of different +depths, attempt to use the specified one (in bits per pixel); if +successful, this depth will be requested from the VNC server. +.TP +\fB\-compresslevel \fIlevel\fR +Use specified compression \fIlevel\fR (0..9) for "tight" and "zlib" +encodings (TightVNC\-specific). Level 1 uses minimum of CPU time and +achieves weak compression ratios, while level 9 offers best +compression but is slow in terms of CPU time consumption on the server +side. Use high levels with very slow network connections, and low +levels when working over high\-speed LANs. It's not recommended to use +compression level 0, reasonable choices start from the level 1. +.TP +\fB\-quality \fIlevel\fR +Use the specified JPEG quality \fIlevel\fR (0..9) for the "tight" +encoding (TightVNC\-specific). Quality level 0 denotes bad image +quality but very impressive compression ratios, while level 9 offers +very good image quality at lower compression ratios. Note that the +"tight" encoder uses JPEG to encode only those screen areas that look +suitable for lossy compression, so quality level 0 does not always +mean unacceptable image quality. +.TP +\fB\-nojpeg\fR +Disable lossy JPEG compression in Tight encoding (TightVNC\-specific). +Disabling JPEG compression is not a good idea in typical cases, as +that makes the Tight encoder less efficient. You might want to use +this option if it's absolutely necessary to achieve perfect image +quality (see also the \fB\-quality\fR option). +.TP +\fB\-nocursorshape\fR +Disable cursor shape updates, protocol extensions used to handle +remote cursor movements locally on the client side +(TightVNC\-specific). Using cursor shape updates decreases delays with +remote cursor movements, and can improve bandwidth usage dramatically. +.TP +\fB\-x11cursor\fR +Use a real X11 cursor with X-style cursor shape updates, instead of +drawing the remote cursor on the framebuffer. This option also +disables the dot cursor, and disables cursor position updates in +non-fullscreen mode. +.TP +\fB\-autopass\fR +Read a plain-text password from stdin. This option affects only the +standard VNC authentication. + +.SH Enhanced TightVNC Viewer (SSVNC) OPTIONS +.TP +Enhanced TightVNC Viewer (SSVNC) web page is located at: +.TP +http://www.karlrunge.com/x11vnc/ssvnc.html +.TP +Note: ZRLE and ZYWRLE encodings are now supported. +.TP +Note: F9 is shortcut to Toggle FullScreen mode. +.TP +Note: In -listen mode set the env var. SSVNC_MULTIPLE_LISTEN=1 +to allow more than one incoming VNC server at a time. +This is the same as -multilisten described below. Set +SSVNC_MULTIPLE_LISTEN=MAX:n to allow no more than "n" +simultaneous reverse connections. + +If the host:port is specified as "exec=command args..." +then instead of making a TCP/IP socket connection to the +remote VNC server, "command args..." is executed and the +viewer is attached to its stdio. This enables tunnelling +established via an external command, e.g. an stunnel(8) +that does not involve a listening socket. +This mode does not work for -listen reverse connections. + +If the host:port is specified as "fd=n" then it is assumed +n is an already opened file descriptor to the socket. (i.e +the parent did fork+exec) + +If the host:port contains a '/' it is interpreted as a +unix-domain socket (AF_LOCAL insead of AF_INET) +.TP +\fB\-multilisten\fR +As in -listen (reverse connection listening) except +allow more than one incoming VNC server to be connected +at a time. The default for -listen of only one at a +time tries to play it safe by not allowing anyone on +the network to put (many) desktops on your screen over +a long window of time. Use -multilisten for no limit. +.TP +\fB\-acceptpopup\fR +In \fB\-listen\fR (reverse connection listening) mode when +a reverse VNC connection comes in show a popup asking +whether to Accept or Reject the connection. The IP +address of the connecting host is shown. Same as +setting the env. var. SSVNC_ACCEPT_POPUP=1. +.TP +\fB\-acceptpopupsc\fR +As in \fB\-acceptpopup\fR except assume UltraVNC Single +Click (SC) server. Retrieve User and ComputerName +info from UltraVNC Server and display in the Popup. +.TP +\fB\-use64\fR +In \fB\-bgr233\fR mode, use 64 colors instead of 256. +.TP +\fB\-bgr222\fR +Same as \fB\-use64\fR. +.TP +\fB\-use8\fR +In \fB\-bgr233\fR mode, use 8 colors instead of 256. +.TP +\fB\-bgr111\fR +Same as \fB\-use8\fR. +.TP +\fB\-16bpp\fR +If the vnc viewer X display is depth 24 at 32bpp +request a 16bpp format from the VNC server to cut +network traffic by up to 2X, then tranlate the +pixels to 32bpp locally. +.TP +\fB\-bgr565\fR +Same as \fB\-16bpp\fR. +.TP +\fB\-grey\fR +Use a grey scale for the 16- and 8\fB\-bpp\fR modes. +.TP +\fB\-alpha\fR +Use alphablending transparency for local cursors +requires: x11vnc server, both client and server +must be 32bpp and same endianness. +.TP +\fB\-scale\fR \fIstr\fR +Scale the desktop locally. The string "str" can +a floating point ratio, e.g. "0.9", or a fraction, +e.g. "3/4", or WxH, e.g. 1280x1024. Use "fit" +to fit in the current screen size. Use "auto" to +fit in the window size. "str" can also be set by +the env. var. SSVNC_SCALE. + +If you observe mouse trail painting errors, enable +X11 Cursor mode (either via Popup or \fB\-x11cursor\fR.) + +Note that scaling is done in software and so can be +slow and requires more memory. Some speedup Tips: + +ZRLE is faster than Tight in this mode. When +scaling is first detected, the encoding will +be automatically switched to ZRLE. Use the +Popup menu if you want to go back to Tight. +Set SSVNC_PRESERVE_ENCODING=1 to disable this. + +Use a solid background on the remote side. +(e.g. manually or via x11vnc \fB\-solid\fR ...) + +If the remote server is x11vnc, try client +side caching: x11vnc \fB\-ncache\fR 10 ... +.TP +\fB\-ycrop\fR n +Only show the top n rows of the framebuffer. For +use with x11vnc \fB\-ncache\fR client caching option +to help "hide" the pixel cache region. +Use a negative value (e.g. \fB\-1\fR) for autodetection. +Autodetection will always take place if the remote +fb height is more than 2 times the width. +.TP +\fB\-sbwidth\fR n +Scrollbar width for x11vnc \fB\-ncache\fR mode (\fB\-ycrop\fR), +default is very narrow: 2 pixels, it is narrow to +avoid distraction in \fB\-ycrop\fR mode. +.TP +\fB\-nobell\fR +Disable bell. +.TP +\fB\-rawlocal\fR +Prefer raw encoding for localhost, default is +no, i.e. assumes you have a SSH tunnel instead. +.TP +\fB\-notty\fR +Try to avoid using the terminal for interactive +responses: use windows for messages and prompting +instead. Messages will also be printed to terminal. +.TP +\fB\-sendclipboard\fR +Send the X CLIPBOARD selection (i.e. Ctrl+C, +Ctrl+V) instead of the X PRIMARY selection (mouse +select and middle button paste.) +.TP +\fB\-sendalways\fR +Whenever the mouse enters the VNC viewer main +window, send the selection to the VNC server even if +it has not changed. This is like the Xt resource +translation SelectionToVNC(always) +.TP +\fB\-recvtext\fR +str When cut text is received from the VNC server, +ssvncviewer will set both the X PRIMARY and the +X CLIPBOARD local selections. To control which +is set, specify 'str' as 'primary', 'clipboard', +or 'both' (the default.) +.TP +\fB\-graball\fR +Grab the entire X server when in fullscreen mode, +needed by some old window managers like fvwm2. +.TP +\fB\-popupfix\fR +Warp the popup back to the pointer position, +needed by some old window managers like fvwm2. +.TP +\fB\-grabkbd\fR +Grab the X keyboard when in fullscreen mode, +needed by some window managers. Same as \fB\-grabkeyboard\fR. +\fB\-grabkbd\fR is the default, use \fB\-nograbkbd\fR to disable. +.TP +\fB\-bs\fR, \fB\-nobs\fR +Whether or not to use X server Backingstore for the +main viewer window. The default is to not, mainly +because most Linux, etc, systems X servers disable +*all* Backingstore by default. To re\fB\-enable\fR it put + +Option "Backingstore" + +in the Device section of /etc/X11/xorg.conf. +In \fB\-bs\fR mode with no X server backingstore, whenever an +area of the screen is re\fB\-exposed\fR it must go out to the +VNC server to retrieve the pixels. This is too slow. + +In \fB\-nobs\fR mode, memory is allocated by the viewer to +provide its own backing of the main viewer window. This +actually makes some activities faster (changes in large +regions) but can appear to "flash" too much. +.TP +\fB\-noshm\fR +Disable use of MIT shared memory extension (not recommended) +.TP +\fB\-termchat\fR +Do the UltraVNC chat in the terminal vncviewer is in +instead of in an independent window. +.TP +\fB\-unixpw\fR \fIstr\fR +Useful for logging into x11vnc in \fB\-unixpw\fR mode. "str" is a +string that allows many ways to enter the Unix Username +and Unix Password. These characters: username, newline, +password, newline are sent to the VNC server after any VNC +authentication has taken place. Under x11vnc they are +used for the \fB\-unixpw\fR login. Other VNC servers could do +something similar. + +You can also indicate "str" via the environment +variable SSVNC_UNIXPW. + +Note that the Escape key is actually sent first to tell +x11vnc to not echo the Unix Username back to the VNC +viewer. Set SSVNC_UNIXPW_NOESC=1 to override this. + +If str is ".", then you are prompted at the command line +for the username and password in the normal way. If str is +"-" the stdin is read via getpass(3) for username@password. +Otherwise if str is a file, it is opened and the first line +read is taken as the Unix username and the 2nd as the +password. If str prefixed by "rm:" the file is removed +after reading. Otherwise, if str has a "@" character, +it is taken as username@password. Otherwise, the program +exits with an error. Got all that? +.TP +\fB-repeater\fR \fIstr\fR +This is for use with UltraVNC repeater proxy described +here: http://www.uvnc.com/addons/repeater.html. The "str" +is the ID string to be sent to the repeater. E.g. ID:1234 +It can also be the hostname and port or display of the VNC +server, e.g. 12.34.56.78:0 or snoopy.com:1. Note that when +using -repeater, the host:dpy on the cmdline is the repeater +server, NOT the VNC server. The repeater will connect you. + +Example: vncviewer ... -repeater ID:3333 repeat.host:5900 + +Example: vncviewer ... -repeater vhost:0 repeat.host:5900 + +Use, e.g., '-repeater SCIII=ID:3210' if the repeater is a +Single Click III (SSL) repeater (repeater_SSL.exe) and you +are passing the SSL part of the connection through stunnel, socat, etc. +This way the magic UltraVNC string 'testB' needed to work with the +repeater is sent to it. +.TP +\fB-rfbversion\fR \fIstr\fR +Set the advertised RFB version. E.g.: -rfbversion 3.6 For some +servers, e.g. UltraVNC this needs to be done. +.TP +\fB-ultradsm\fR +UltraVNC has symmetric private encryption DSM plugins. See +http://www.uvnc.com/features/encryption.html. It is assumed +you are using a unix program (e.g. our ultravnc_dsm_helper) to +encrypt and decrypt the UltraVNC DSM stream. IN ADDITION TO +THAT supply -ultradsm to tell THIS viewer to modify the RFB +data sent so as to work with the UltraVNC Server. For some +reason, each RFB msg type must be sent twice under DSM. +.TP +\fB\-mslogon\fR \fIuser\fR +Use Windows MS Logon to an UltraVNC server. Supply the +username or "1" to be prompted. The default is to +autodetect the UltraVNC MS Logon server and prompt for +the username and password. + +IMPORTANT NOTE: The UltraVNC MS-Logon Diffie-Hellman +exchange is very weak and can be brute forced to recover +your username and password in a few seconds of CPU +time. To be safe, be sure to use an additional encrypted +tunnel (e.g. SSL or SSH) for the entire VNC session. +.TP +\fB\-chatonly\fR +Try to be a client that only does UltraVNC text chat. This +mode is used by x11vnc to present a chat window on the physical +X11 console (i.e. to chat with the person at the display). +.TP +\fB-env\fR \fIVAR=VALUE\fR +To save writing a shell script to set environment +variables, specify as many as you need on the command line. For example, +-env SSVNC_MULTIPLE_LISTEN=MAX:5 -env EDITOR=vi +.TP +\fB\-noipv6\fR +Disable all IPv6 sockets. Same as VNCVIEWER_NO_IPV6=1. +.TP +\fB\-noipv4\fR +Disable all IPv4 sockets. Same as VNCVIEWER_NO_IPV4=1. +.TP +\fB\-printres\fR +Print out the Ssvnc X resources (appdefaults) and +then exit. You can save them to a file and customize them (e.g. the +keybindings and Popup menu) Then point to the file via +XENVIRONMENT or XAPPLRESDIR. +.TP +\fB\-pipeline\fR +Like TurboVNC, request the next framebuffer update as soon +as possible instead of waiting until the end of the current +framebuffer update coming in. Helps 'pipeline' the updates. +This is currently the default, use \fB-nopipeline\fR to disable. +.TP +\fB\-appshare\fR +Enable features for use with x11vnc's \fB\-appshare\fR mode where +instead of sharing the full desktop only the application's +windows are shared. Viewer multilisten mode is used to +create the multiple windows: \fB\-multilisten\fR is implied. +See 'x11vnc \fB\-appshare\fR \fB\-help\fR' more information on the mode. +Features enabled in the viewer under \fB\-appshare\fR are: +Minimum extra text in the title, auto \fB\-ycrop\fR is disabled, +x11vnc \fB\-remote_prefix\fR X11VNC_APPSHARE_CMD: message channel, +x11vnc initial window position hints. See also Escape Keys +below for additional key and mouse bindings. +.TP +\fB\-escape \fR\fIstr\fR +This sets the 'Escape Keys' modifier sequence and enables +escape keys mode. When the modifier keys escape sequence +is held down, the next keystroke is interpreted locally +to perform a special action instead of being sent to the +remote VNC server. + +Use '\fB\-escape\fR default' for the default modifier sequence. +(Unix: Alt_L,Super_L and MacOSX: Control_L,Meta_L) + +Here are the 'Escape Keys: Help+Set' instructions from the Popup: + +Escape Keys: Enter a comma separated list of modifier keys to be the 'escape +sequence'. When these keys are held down, the next keystroke is +interpreted locally to invoke a special action instead of being sent to +the remote VNC server. In other words, a set of 'Hot Keys'. + +Here is the list of local key mappings to special actions: + +r: refresh desktop b: toggle bell c: toggle full-color + +f: file transfer x: x11cursor z: toggle Tight/ZRLE + +l: full screen g: graball e: escape keys dialog + +s: scale dialog +: scale up (=) -: scale down (_) + +t: text chat a: alphablend cursor + +V: toggle viewonly Q: quit viewer 123456: UltraVNC scale 1/n + +Arrow keys: pan the viewport about 10% for each keypress. + +PageUp/PageDown: pan the viewport by a screenful vertically. + +Home/End: pan the viewport by a screenful horizontally. + +KeyPad Arrows: pan the viewport by 1 pixel for each keypress. + +Dragging the Mouse with Button1 pressed also pans the viewport. + +Clicking Mouse Button3 brings up the Popup Menu. + +The above mappings are \fBalways\fR active in ViewOnly mode, unless you set +the Escape Keys value to 'never'. + +x11vnc -appshare hot-keys: x11vnc has a simple application sharing mode +that enables the viewer-side to move, resize, or raise the remote toplevel +windows. To enable it, hold down Shift + the Escape Keys and press these: + +Arrow keys: move the remote window around in its desktop. + +PageUp/PageDn/Home/End: resize the remote window. + ++/-: raise or lower the remote window. + +M or Button1 move win to local position; D or Button3: delete remote win. + +If the Escape Keys value below is set to 'default' then a default list of +of modifier keys is used. For Unix it is: Alt_L,Super_L and for MacOSX it +is Control_L,Meta_L. Note: the Super_L key usually has a Windows(TM) Flag +on it. Also note the _L and _R mean the key is on the LEFT or RIGHT side +of the keyboard. + +On Unix the default is Alt and Windows keys on Left side of keyboard. +On MacOSX the default is Control and Command keys on Left side of keyboard. + +Example: Press and hold the Alt and Windows keys on the LEFT side of the +keyboard and then press 'c' to toggle the full-color state. Or press 't' +to toggle the ultravnc Text Chat window, etc. + +To use something besides the default, supply a comma separated list (or a +single one) from: Shift_L Shift_R Control_L Control_R Alt_L Alt_R Meta_L +Meta_R Super_L Super_R Hyper_L Hyper_R or Mode_switch. +.TP +\fB New Popup actions:\fR + + ViewOnly: ~ -viewonly + Disable Bell: ~ -nobell + Cursor Shape: ~ -nocursorshape + X11 Cursor: ~ -x11cursor + Cursor Alphablend: ~ -alpha + Toggle Tight/Hextile: ~ -encodings hextile... + Toggle Tight/ZRLE: ~ -encodings zrle... + Toggle ZRLE/ZYWRLE: ~ -encodings zywrle... + Quality Level ~ -quality (both Tight and ZYWRLE) + Compress Level ~ -compresslevel + Disable JPEG: ~ -nojpeg (Tight) + Pipeline Updates ~ -pipeline + + Full Color as many colors as local screen allows. + Grey scale (16 & 8-bpp) ~ -grey, for low colors 16/8bpp modes only. + 16 bit color (BGR565) ~ -16bpp / -bgr565 + 8 bit color (BGR233) ~ -bgr233 + 256 colors ~ -bgr233 default # of colors. + 64 colors ~ -bgr222 / -use64 + 8 colors ~ -bgr111 / -use8 + Scale Viewer ~ -scale + Escape Keys: Toggle ~ -escape + Escape Keys: Help+Set ~ -escape + Set Y Crop (y-max) ~ -ycrop + Set Scrollbar Width ~ -sbwidth + XGrabServer ~ -graball + + UltraVNC Extensions: + + Set 1/n Server Scale Ultravnc ext. Scale desktop by 1/n. + Text Chat Ultravnc ext. Do Text Chat. + File Transfer Ultravnc ext. File xfer via Java helper. + Single Window Ultravnc ext. Grab and view a single window. + (select then click on the window you want). + Disable Remote Input Ultravnc ext. Try to prevent input and + viewing of monitor at physical display. + + Note: the Ultravnc extensions only apply to servers that support + them. x11vnc/libvncserver supports some of them. + + Send Clipboard not Primary ~ -sendclipboard + Send Selection Every time ~ -sendalways + +.SH ENCODINGS +The server supplies information in whatever format is desired by the +client, in order to make the client as easy as possible to implement. +If the client represents itself as able to use multiple formats, the +server will choose one. + +.I Pixel format +refers to the representation of an individual pixel. The most common +formats are 24 and 16 bit "true\-color" values, and 8\-bit "color map" +representations, where an arbitrary map converts the color number to +RGB values. + +.I Encoding +refers to how a rectangle of pixels are sent (all pixel information in +VNC is sent as rectangles). All rectangles come with a header giving +the location and size of the rectangle and an encoding type used by +the data which follows. These types are listed below. +.TP +.B Raw +The raw encoding simply sends width*height pixel values. All clients +are required to support this encoding type. Raw is also the fastest +when the server and viewer are on the same machine, as the connection +speed is essentially infinite and raw encoding minimizes processing +time. +.TP +.B CopyRect +The Copy Rectangle encoding is efficient when something is being +moved; the only data sent is the location of a rectangle from which +data should be copied to the current location. Copyrect could also be +used to efficiently transmit a repeated pattern. +.TP +.B RRE +The Rise\-and\-Run\-length\-Encoding is basically a 2D version of +run\-length encoding (RLE). In this encoding, a sequence of identical +pixels are compressed to a single value and repeat count. In VNC, this +is implemented with a background color, and then specifications of an +arbitrary number of subrectangles and color for each. This is an +efficient encoding for large blocks of constant color. +.TP +.B CoRRE +This is a minor variation on RRE, using a maximum of 255x255 pixel +rectangles. This allows for single\-byte values to be used, reducing +packet size. This is in general more efficient, because the savings +from sending 1\-byte values generally outweighs the losses from the +(relatively rare) cases where very large regions are painted the same +color. +.TP +.B Hextile +Here, rectangles are split up in to 16x16 tiles, which are sent in a +predetermined order. The data within the tiles is sent either raw or +as a variant on RRE. Hextile encoding is usually the best choice for +using in high\-speed network environments (e.g. Ethernet local\-area +networks). +.TP +.B Zlib +Zlib is a very simple encoding that uses zlib library to compress raw +pixel data. This encoding achieves good compression, but consumes a +lot of CPU time. Support for this encoding is provided for +compatibility with VNC servers that might not understand Tight +encoding which is more efficient than Zlib in nearly all real\-life +situations. +.TP +.B Tight +Like Zlib encoding, Tight encoding uses zlib library to compress the +pixel data, but it pre\-processes data to maximize compression ratios, +and to minimize CPU usage on compression. Also, JPEG compression may +be used to encode color\-rich screen areas (see the description of +\-quality and \-nojpeg options above). Tight encoding is usually the +best choice for low\-bandwidth network environments (e.g. slow modem +connections). +.TP +.B ZRLE +The SSVNC viewer has ported the RealVNC (www.realvnc.com) ZRLE encoding +to the unix tightvnc viewer. +.TP +.B ZYWRLE +The SSVNC viewer has ported the Hitachi lossy wavelet based ZRLE +encoding from http://mobile.hitachi-system.co.jp/publications/ZYWRLE/ +to the unix tightvnc viewer. +.SH RESOURCES +X resources that \fBvncviewer\fR knows about, aside from the +normal Xt resources, are as follows: +.TP +.B shareDesktop +Equivalent of \fB\-shared\fR/\fB\-noshared\fR options. Default true. +.TP +.B viewOnly +Equivalent of \fB\-viewonly\fR option. Default false. +.TP +.B fullScreen +Equivalent of \fB\-fullscreen\fR option. Default false. +.TP +.B grabKeyboard +Grab keyboard in full-screen mode. This can help to solve problems +with losing keyboard focus. Default false. +.TP +.B raiseOnBeep +Equivalent of \fB\-noraiseonbeep\fR option, when set to false. Default +true. +.TP +.B passwordFile +Equivalent of \fB\-passwd\fR option. +.TP +.B userLogin +Equivalent of \fB\-user\fR option. +.TP +.B passwordDialog +Whether to use a dialog box to get the password (true) or get it from +the tty (false). Irrelevant if \fBpasswordFile\fR is set. Default +false. +.TP +.B encodings +Equivalent of \fB\-encodings\fR option. +.TP +.B compressLevel +Equivalent of \fB\-compresslevel\fR option (TightVNC\-specific). +.TP +.B qualityLevel +Equivalent of \fB\-quality\fR option (TightVNC\-specific). +.TP +.B enableJPEG +Equivalent of \fB\-nojpeg\fR option, when set to false. Default true. +.TP +.B useRemoteCursor +Equivalent of \fB\-nocursorshape\fR option, when set to false +(TightVNC\-specific). Default true. +.TP +.B useBGR233 +Equivalent of \fB\-bgr233\fR option. Default false. +.TP +.B nColours +When using BGR233, try to allocate this many "exact" colors from the +BGR233 color cube. When using a shared colormap, setting this resource +lower leaves more colors for other X clients. Irrelevant when using +truecolor. Default is 256 (i.e. all of them). +.TP +.B useSharedColours +If the number of "exact" BGR233 colors successfully allocated is less +than 256 then the rest are filled in using the "nearest" colors +available. This resource says whether to only use the "exact" BGR233 +colors for this purpose, or whether to use other clients' "shared" +colors as well. Default true (i.e. use other clients' colors). +.TP +.B forceOwnCmap +Equivalent of \fB\-owncmap\fR option. Default false. +.TP +.B forceTrueColour +Equivalent of \fB\-truecolour\fR option. Default false. +.TP +.B requestedDepth +Equivalent of \fB\-depth\fR option. +.TP +.B useSharedMemory +Use MIT shared memory extension if on the same machine as the X +server. Default true. +.TP +.B wmDecorationWidth, wmDecorationHeight +The total width and height taken up by window manager decorations. +This is used to calculate the maximum size of the VNC viewer window. +Default is width 4, height 24. +.TP +.B bumpScrollTime, bumpScrollPixels +When in full screen mode and the VNC desktop is bigger than the X +display, scrolling happens whenever the mouse hits the edge of the +screen. The maximum speed of scrolling is bumpScrollPixels pixels +every bumpScrollTime milliseconds. The actual speed of scrolling will +be slower than this, of course, depending on how fast your machine is. +Default 20 pixels every 25 milliseconds. +.TP +.B popupButtonCount +The number of buttons in the popup window. See the README file for +more information on how to customize the buttons. +.TP +.B debug +For debugging. Default false. +.TP +.B rawDelay, copyRectDelay +For debugging, see the README file for details. Default 0 (off). +.SH ENVIRONMENT +When started with the \fB\-via\fR option, vncviewer reads the +\fBVNC_VIA_CMD\fR environment variable, expands patterns beginning +with the "%" character, and executes result as a command assuming that +it would create TCP tunnel that should be used for VNC connection. If +not set, this environment variable defaults to "/usr/bin/ssh -f -L +%L:%H:%R %G sleep 20". + +The following patterns are recognized in the \fBVNC_VIA_CMD\fR (note +that all the patterns %G, %H, %L and %R must be present in the command +template): +.TP +.B %% +A literal "%"; +.TP +.B %G +gateway host name; +.TP +.B %H +remote VNC host name, as known to the gateway; +.TP +.B %L +local TCP port number; +.TP +.B %R +remote TCP port number. +.SH SEE ALSO +\fBvncserver\fR(1), \fBx11vnc\fR(1), \fBssvnc\fR(1), \fBXvnc\fR(1), \fBvncpasswd\fR(1), +\fBvncconnect\fR(1), \fBssh\fR(1), http://www.karlrunge.com/x11vnc, http://www.karlrunge.com/x11vnc/ssvnc.html +.SH AUTHORS +Original VNC was developed in AT&T Laboratories Cambridge. TightVNC +additions was implemented by Constantin Kaplinsky. Many other people +participated in development, testing and support. Karl J. Runge +added all of the SSVNC related features and improvements. + +\fBMan page authors:\fR +.br +Marcus Brinkmann <Marcus.Brinkmann@ruhr-uni-bochum.de>, +.br +Terran Melconian <terran@consistent.org>, +.br +Tim Waugh <twaugh@redhat.com>, +.br +Constantin Kaplinsky <const@ce.cctpu.edu.ru> +.br +Karl J. Runge <runge@karlrunge.com> diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/vncviewer.c vnc_unixsrc/vncviewer/vncviewer.c --- vnc_unixsrc.orig/vncviewer/vncviewer.c 2004-01-13 09:22:05.000000000 -0500 +++ vnc_unixsrc/vncviewer/vncviewer.c 2010-04-18 12:43:47.000000000 -0400 @@ -22,6 +22,8 @@ */ #include "vncviewer.h" +#include <ctype.h> +#include <X11/Xaw/Toggle.h> char *programName; XtAppContext appContext; @@ -29,11 +31,274 @@ Widget toplevel; +extern void raiseme(int force); +extern void CreateChat(void); + +void set_sbwidth(int sbw) { + char *q, *p, t[5]; + int i, k, N = 4; + int db = 0; + + if (sbw < 1) { + sbw = 2; + } else if (sbw > 100) { + sbw = 100; + } + if (db) fprintf(stderr, "sbw: %d\n", sbw); + + sprintf(t, "%4d", sbw); + k = 0; + while (fallback_resources[k] != NULL) { + q = strstr(fallback_resources[k], "horizontal.height: "); + if (!q) { + q = strstr(fallback_resources[k], "vertical.width: "); + } + if (q) { + p = strdup(fallback_resources[k]); + q = strstr(p, ": "); + if (q) { + q++; + q++; + for (i=0; i < N; i++) { + *(q+i) = t[i]; + } + fallback_resources[k] = p; + if (db) fprintf(stderr, "res: %s\n\n", p); + } + } + k++; + } +} + +void min_title(void) { + char *q; + int k; + + k = 0; + while (fallback_resources[k] != NULL) { + q = strstr(fallback_resources[k], "Ssvnc.title: "); + if (q) { + fallback_resources[k] = strdup("Ssvnc.title: %s"); + } + k++; + } +} + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +void unixpw(char *instr, int vencrypt_plain) { + char *str, *q, *infile = NULL; + FILE *in; + int i, rmfile = 0; + struct stat sb; + int N = 99; + char username[100], passwd[100]; + static int did = 0; + + if (did) { + return; + } + did = 1; + + for (i=0; i<100; i++) { + username[i] = '\0'; + passwd[i] = '\0'; + } + + if (instr == NULL) { + return; + } else if (!strcmp(instr, "")) { + return; + } + + str = strdup(instr); + + if (strstr(str, "rm:") == str) { + rmfile = 1; + infile = str + strlen("rm:"); + } else if (stat(str, &sb) == 0) { + infile = str; + } + if (!strcmp(str, ".")) { + char *p; + if (!use_tty()) { + char *u; + fprintf(stderr, "\nEnter Unix Username and Password in the popups.\n"); + u = DoUserDialog(); + if (strlen(u) >= 100) { + exit(1); + } + sprintf(username, u); + p = DoPasswordDialog(); + } else { + raiseme(1); + fprintf(stderr, "\nUnix Username: "); + if (fgets(username, N, stdin) == NULL) { + exit(1); + } + p = getpass("Unix Password: "); + } + if (! p) { + exit(1); + } + strncpy(passwd, p, N); + fprintf(stderr, "\n"); + + } else if (!strcmp(str, "-")) { + char *p, *q; + if (!use_tty()) { + fprintf(stderr, "\nEnter unixuser@unixpasswd in the popup.\n"); + p = DoPasswordDialog(); + } else { + raiseme(1); + p = getpass("unixuser@unixpasswd: "); + } + if (! p) { + exit(1); + } + q = strchr(p, '@'); + if (! q) { + exit(1); + } + *q = '\0'; + strncpy(username, p, N); + strncpy(passwd, q+1, N); + + } else if (infile) { + in = fopen(infile, "r"); + if (in == NULL) { + fprintf(stderr, "failed to open -unixpw file.\n"); + exit(1); + } + if (fgets(username, N, in) == NULL) { + exit(1); + } + if (fgets(passwd, N, in) == NULL) { + exit(1); + } + fclose(in); + fprintf(stderr, "read username@passwd from file: %s\n", infile); + if (rmfile) { + fprintf(stderr, "deleting username@passwd file: %s\n", infile); + unlink(infile); + } + } else if (strchr(str, '@')) { + char *q = strchr(str, '@'); + *q = '\0'; + strncpy(username, str, N); + strncpy(passwd, q+1, N); + } else { + exit(1); + } + + free(str); + + if (vencrypt_plain) { + CARD32 ulen, plen; + char *q; + + q = strrchr(username, '\n'); + if (q) *q = '\0'; + q = strrchr(passwd, '\n'); + if (q) *q = '\0'; + + ulen = Swap32IfLE((CARD32)strlen(username)); + plen = Swap32IfLE((CARD32)strlen(passwd)); + + if (!WriteExact(rfbsock, (char *)&ulen, 4) || + !WriteExact(rfbsock, (char *)&plen, 4)) { + return; + } + + if (!WriteExact(rfbsock, username, strlen(username)) || + !WriteExact(rfbsock, passwd, strlen(passwd))) { + return; + } + return; + } + + + if (! getenv("SSVNC_UNIXPW_NOESC")) { + SendKeyEvent(XK_Escape, 1); + SendKeyEvent(XK_Escape, 0); + } + + q = username; + while (*q != '\0' && *q != '\n') { + char c = *q; + if (c >= 0x20 && c <= 0x07e) { + KeySym ks = (KeySym) c; + SendKeyEvent(ks, 1); + SendKeyEvent(ks, 0); + } + q++; + } + + SendKeyEvent(XK_Return, 1); + SendKeyEvent(XK_Return, 0); + + q = passwd; + while (*q != '\0' && *q != '\n') { + char c = *q; + if (c >= 0x20 && c <= 0x07e) { + KeySym ks = (KeySym) c; + SendKeyEvent(ks, 1); + SendKeyEvent(ks, 0); + } + q++; + } + + SendKeyEvent(XK_Return, 1); + SendKeyEvent(XK_Return, 0); +} + +static void chat_window_only(void) { + if (appData.chatOnly) { + static double last_time = 0.0; + if (dnow() > last_time + 1.5) { + XSync(dpy, False); + XUnmapWindow(dpy, XtWindow(toplevel)); + } + } +} + +int saw_appshare = 0; + int main(int argc, char **argv) { - int i; - programName = argv[0]; + int i, save_sbw, saw_listen = 0; + char *pw_loc = NULL; + programName = argv[0]; + + if (strrchr(programName, '/') != NULL) { + programName = strrchr(programName, '/') + 1; + } + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-env")) { + if (i+1 < argc) { + char *estr = argv[i+1]; + if (strchr(estr, '=')) { + putenv(estr); + } + } + } + if (!strcmp(argv[i], "-noipv4")) { + putenv("VNCVIEWER_NO_IPV4=1"); + } + if (!strcmp(argv[i], "-noipv6")) { + putenv("VNCVIEWER_NO_IPV6=1"); + } + } + if (getenv("VNCVIEWER_NO_IPV4")) { + appData.noipv4 = True; + } + if (getenv("VNCVIEWER_NO_IPV6")) { + appData.noipv6 = True; + } /* The -listen option is used to make us a daemon process which listens for incoming connections from servers, rather than actively connecting to a @@ -45,89 +310,1744 @@ listenForIncomingConnections() returns, setting the listenSpecified flag. */ - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-listen") == 0) { - listenForIncomingConnections(&argc, argv, i); - break; - } - if (strcmp(argv[i], "-tunnel") == 0 || strcmp(argv[i], "-via") == 0) { - if (!createTunnel(&argc, argv, i)) - exit(1); - break; - } - } + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-appshare")) { + putenv("SSVNC_MULTIPLE_LISTEN=1"); + fprintf(stderr, "Enabling -multilisten mode for 'x11vnc -appshare' usage.\n\n"); + saw_appshare = 1; + } + if (!strcmp(argv[i], "-multilisten")) { + putenv("SSVNC_MULTIPLE_LISTEN=1"); + saw_listen = 2; + } + if (!strcmp(argv[i], "-listen")) { + saw_listen = 1; + } + if (!strcmp(argv[i], "-acceptpopup")) { + putenv("SSVNC_ACCEPT_POPUP=1"); + } + if (!strcmp(argv[i], "-acceptpopupsc")) { + putenv("SSVNC_ACCEPT_POPUP_SC=1"); + } + if (strstr(argv[i], " pw=") != NULL) { + pw_loc = strstr(argv[i], " pw=") + 1; + } + } + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-appshare") && !saw_listen) { + listenForIncomingConnections(&argc, argv, i); + break; + } + if (!strcmp(argv[i], "-multilisten")) { + listenForIncomingConnections(&argc, argv, i); + break; + } + if (!strcmp(argv[i], "-listen")) { + listenForIncomingConnections(&argc, argv, i); + break; + } + if (!strcmp(argv[i], "-tunnel") || !strcmp(argv[i], "-via")) { + if (!createTunnel(&argc, argv, i)) { + exit(1); + } + break; + } + if (!strcmp(argv[i], "-printres") || !strcmp(argv[i], "-res")) { + int j = 0; + fprintf(stdout, "\n! Ssvnc fallback X resources:\n\n"); + while (1) { + char *p = fallback_resources[j++]; + int k = 0; + if (p == NULL) break; + while (*p != '\0') { + fprintf(stdout, "%c", *p); + if (k > 0 && *p == 'n' && *(p-1) == '\\') { + fprintf(stdout, "\\\n"); + } + p++; k++; + } + fprintf(stdout, "\n\n"); + } + exit(0); + } + } + + + if (argc > 1 && strstr(argv[1], "-h") == argv[1]) { + usage(); + return 0; + } /* Call the main Xt initialisation function. It parses command-line options, generating appropriate resource specs, and makes a connection to the X display. */ - toplevel = XtVaAppInitialize(&appContext, "Vncviewer", - cmdLineOptions, numCmdLineOptions, - &argc, argv, fallback_resources, - XtNborderWidth, 0, NULL); + if (saw_appshare || getenv("VNCVIEWER_MIN_TITLE")) { + min_title(); + } + appData.sbWidth = 0; + if (getenv("VNCVIEWER_SBWIDTH")) { + int sbw = atoi(getenv("VNCVIEWER_SBWIDTH")); + if (sbw > 0) { + appData.sbWidth = sbw; + } + } + if (appData.sbWidth == 0) { + int i, sbw = 0; + for (i = 1; i < argc - 1; i++) { + if (!strcmp(argv[i], "-sbwidth")) { + sbw = atoi(argv[i+1]); + } + } + if (sbw > 0) { + appData.sbWidth = sbw; + } + } + save_sbw = appData.sbWidth; + if (save_sbw > 0) { + set_sbwidth(save_sbw); + } else { + set_sbwidth(6); + } + + toplevel = XtVaAppInitialize(&appContext, "Ssvnc", cmdLineOptions, + numCmdLineOptions, &argc, argv, fallback_resources, + XtNborderWidth, 0, NULL); - dpy = XtDisplay(toplevel); + dpy = XtDisplay(toplevel); /* Interpret resource specs and process any remaining command-line arguments (i.e. the VNC server name). If the server name isn't specified on the command line, getArgsAndResources() will pop up a dialog box and wait for one to be entered. */ - GetArgsAndResources(argc, argv); + GetArgsAndResources(argc, argv); + + if (saw_appshare) { + appData.appShare = True; + } + + if (save_sbw) { + appData.sbWidth = save_sbw; + } + + if (appData.chatOnly) { + appData.encodingsString = "raw hextile"; + } + + if (pw_loc != NULL) { + char *q = pw_loc; + while (*q != '\0' && !isspace(*q)) { + *q = ' '; + q++; + } + } /* Unless we accepted an incoming connection, make a TCP connection to the given VNC server */ - if (!listenSpecified) { - if (!ConnectToRFBServer(vncServerHost, vncServerPort)) exit(1); - } + if (appData.repeaterUltra == NULL) { + if (getenv("SSVNC_REPEATER") != NULL) { + appData.repeaterUltra = strdup(getenv("SSVNC_REPEATER")); + } + } + + if (!listenSpecified) { + if (!ConnectToRFBServer(vncServerHost, vncServerPort)) { + exit(1); + } + if (appData.repeaterUltra != NULL) { + char tmp[256]; + if (strstr(appData.repeaterUltra, "SCIII=") == appData.repeaterUltra) { + appData.repeaterUltra = strdup(appData.repeaterUltra + strlen("SCIII=")); + fprintf(stderr, "sending 'testB' to ultravnc SC III SSL repeater...\n"); + WriteExact(rfbsock, "testB" , 5); + } + if (ReadFromRFBServer(tmp, 12)) { + tmp[12] = '\0'; + fprintf(stderr, "repeater 1st proto line: '%s'\n", tmp); + if (strstr(tmp, "RFB 000.000") == tmp) { + int i; + for (i=0; i<256; i++) { + tmp[i] = '\0'; + } + for (i=0; i<250; i++) { + if (i >= (int) strlen(appData.repeaterUltra)) { + break; + } + tmp[i] = appData.repeaterUltra[i]; + } + fprintf(stderr, "sending '%s' to repeater...\n", tmp); + WriteExact(rfbsock, tmp, 250); + } + } else { + fprintf(stderr, "repeater NO proto line!\n"); + } + } + } /* Initialise the VNC connection, including reading the password */ - if (!InitialiseRFBConnection()) exit(1); + if (!InitialiseRFBConnection()) { + Cleanup(); + exit(1); + } + if (appData.unixPW != NULL) { + unixpw(appData.unixPW, 0); + } else if (getenv("SSVNC_UNIXPW")) { + unixpw(getenv("SSVNC_UNIXPW"), 0); + } /* Create the "popup" widget - this won't actually appear on the screen until some user-defined event causes the "ShowPopup" action to be invoked */ - CreatePopup(); + CreatePopup(); + CreateScaleN(); + CreateTurboVNC(); + CreateQuality(); + CreateCompress(); + CreateChat(); /* Find the best pixel format and X visual/colormap to use */ - SetVisualAndCmap(); + SetVisualAndCmap(); /* Create the "desktop" widget, and perform initialisation which needs doing before the widgets are realized */ - ToplevelInitBeforeRealization(); + ToplevelInitBeforeRealization(); - DesktopInitBeforeRealization(); + DesktopInitBeforeRealization(); /* "Realize" all the widgets, i.e. actually create and map their X windows */ - XtRealizeWidget(toplevel); + XtRealizeWidget(toplevel); /* Perform initialisation that needs doing after realization, now that the X windows exist */ - InitialiseSelection(); + InitialiseSelection(); - ToplevelInitAfterRealization(); + ToplevelInitAfterRealization(); - DesktopInitAfterRealization(); + DesktopInitAfterRealization(); /* Tell the VNC server which pixel format and encodings we want to use */ - SetFormatAndEncodings(); + SetFormatAndEncodings(); + + if (appData.chatOnly) { + chat_window_only(); + ToggleTextChat(0, NULL, NULL, NULL); + } /* Now enter the main loop, processing VNC messages. X events will automatically be processed whenever the VNC connection is idle. */ - while (1) { - if (!HandleRFBServerMessage()) - break; - } + while (1) { + if (!HandleRFBServerMessage()) { + break; + } + if (appData.chatOnly) { + chat_window_only(); + } + } + + Cleanup(); + + return 0; +} + +/* + * Toggle8bpp + */ + +static int last_ncolors = 0; +static int save_useBGR233 = 0; +static Bool save_useBGR565 = False; + +static Widget b8 = NULL; +static Widget b16 = NULL; +static Widget bfull = NULL; + +int do_format_change = 0; +int do_cursor_change = 0; +double do_fb_update = 0.0; +static void schedule_format_change(void) { + do_format_change = 1; + do_cursor_change = 0; +} +extern double dnow(void); +static void schedule_fb_update(void) { + do_fb_update = dnow(); +} +static void init_format_change(void) { + appDataNew.useBGR233 = appData.useBGR233; + appDataNew.useBGR565 = appData.useBGR565; + appDataNew.useGreyScale = appData.useGreyScale; + appDataNew.enableJPEG = appData.enableJPEG; + appDataNew.encodingsString = appData.encodingsString; + appDataNew.useRemoteCursor = appData.useRemoteCursor; + appDataNew.useX11Cursor = appData.useX11Cursor; + appDataNew.useRawLocal = appData.useRawLocal; + appDataNew.qualityLevel = appData.qualityLevel; + appDataNew.compressLevel = appData.compressLevel; +} +void cutover_format_change(void) { + appData.useBGR233 = appDataNew.useBGR233; + appData.useBGR565 = appDataNew.useBGR565; + appData.useGreyScale = appDataNew.useGreyScale; + appData.enableJPEG = appDataNew.enableJPEG; + appData.encodingsString = appDataNew.encodingsString; + appData.useRemoteCursor = appDataNew.useRemoteCursor; + appData.useX11Cursor = appDataNew.useX11Cursor; + appData.useRawLocal = appDataNew.useRawLocal; + appData.qualityLevel = appDataNew.qualityLevel; + appData.compressLevel = appDataNew.compressLevel; +} + +void +Toggle8bpp(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + fprintf(stderr, "Toggle8bpp: %d\n", appData.useBGR233); + b8 = w; + init_format_change(); + if (appData.useBGR233) { + last_ncolors = appData.useBGR233; + appDataNew.useBGR233 = 0; + appDataNew.useBGR565 = save_useBGR565; + fprintf(stderr, "8bpp: off\n"); + } else { + if (!last_ncolors) last_ncolors = 256; + appDataNew.useBGR233 = last_ncolors; + save_useBGR565 = appData.useBGR565; + appDataNew.useBGR565 = False; + fprintf(stderr, "8bpp: on (%d colors)\n", appDataNew.useBGR233); + } + schedule_format_change(); + if (w || ev || params || num_params) {} +} + + +void +Toggle16bpp(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + fprintf(stderr, "Toggle16bpp: %d\n", appData.useBGR565); + b16 = w; + init_format_change(); + if (appData.useBGR565) { + appDataNew.useBGR565 = False; + appDataNew.useBGR233 = save_useBGR233; + fprintf(stderr, "16bpp: off\n"); + } else { + appDataNew.useBGR565 = True; + save_useBGR233 = appData.useBGR233; + appDataNew.useBGR233 = 0; + fprintf(stderr, "16bpp: on\n"); + } + schedule_format_change(); + if (w || ev || params || num_params) {} +} + +void +ToggleFullColor(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + fprintf(stderr, "ToggleFullColor\n"); + bfull = w; + init_format_change(); + if (appData.useBGR565 || appData.useBGR233) { + save_useBGR565 = appData.useBGR565; + appDataNew.useBGR565 = False; + save_useBGR233 = appData.useBGR233; + appDataNew.useBGR233 = 0; + fprintf(stderr, "FullColor: on\n"); + } else { + if (save_useBGR565) { + appDataNew.useBGR565 = True; + appDataNew.useBGR233 = 0; + fprintf(stderr, "FullColor off -> 16bpp.\n"); + } else { + appDataNew.useBGR565 = False; + if (!save_useBGR233) save_useBGR233 = 256; + appDataNew.useBGR233 = save_useBGR233; + fprintf(stderr, "FullColor off -> 8bpp.\n"); + } + } + schedule_format_change(); + if (w || ev || params || num_params) {} +} + +void +ToggleXGrab(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.grabAll) { + appData.grabAll = False; + } else { + appData.grabAll = True; + } + fprintf(stderr, "ToggleXGrab, current=%d\n", appData.grabAll); + /* always ungrab to be sure, fullscreen will handle the rest */ + XUngrabServer(dpy); + if (w || ev || params || num_params) {} +} + +void +ToggleEscapeActive(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.escapeActive) { + appData.escapeActive = False; + } else { + appData.escapeActive = True; + } + if (w || ev || params || num_params) {} +} + +/* + * ToggleNColors + */ + +static Widget w256 = NULL; +static Widget w64 = NULL; +static Widget w8 = NULL; + +void +Toggle256Colors(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + w256 = w; + if (appData.useBGR233 != 256) { + fprintf(stderr, "256 colors: on\n"); + init_format_change(); + last_ncolors = appDataNew.useBGR233 = 256; + save_useBGR565 = appData.useBGR565; + appDataNew.useBGR565 = False; + schedule_format_change(); + } + if (w || ev || params || num_params) {} +} + +void +Toggle64Colors(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + w64 = w; + if (appData.useBGR233 != 64) { + fprintf(stderr, "64 colors: on\n"); + init_format_change(); + last_ncolors = appDataNew.useBGR233 = 64; + save_useBGR565 = appData.useBGR565; + appDataNew.useBGR565 = False; + schedule_format_change(); + } + if (w || ev || params || num_params) {} +} + +void +Toggle8Colors(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + w8 = w; + if (appData.useBGR233 != 8) { + fprintf(stderr, "8 colors: on\n"); + init_format_change(); + last_ncolors = appDataNew.useBGR233 = 8; + save_useBGR565 = appData.useBGR565; + appDataNew.useBGR565 = False; + schedule_format_change(); + } + if (w || ev || params || num_params) {} +} + +void +ToggleGreyScale(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + fprintf(stderr, "ToggleGreyScale\n"); + init_format_change(); + if (appData.useGreyScale) { + appDataNew.useGreyScale = False; + fprintf(stderr, "greyscale: off\n"); + } else { + appDataNew.useGreyScale = True; + fprintf(stderr, "greyscale: on\n"); + } + schedule_format_change(); + if (w || ev || params || num_params) {} +} + +/* + * ToggleJPEG + */ + +void +ToggleJPEG(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + init_format_change(); + if (appData.enableJPEG) { + appDataNew.enableJPEG = False; + fprintf(stderr, "JPEG: off\n"); + } else { + appDataNew.enableJPEG = True; + fprintf(stderr, "JPEG: on\n"); + } + schedule_format_change(); + if (w || ev || params || num_params) {} +} + +/* + * ToggleTightZRLE + */ + +static Bool usingZRLE = False; +static Bool usingZYWRLE = False; +static Bool usingHextile = False; +extern int skip_maybe_sync; + +void +ToggleTightZRLE(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + char prefTight[] = "copyrect tight zrle zywrle zlib hextile corre rre raw"; + char prefZRLE[] = "copyrect zrle zywrle tight zlib hextile corre rre raw"; + init_format_change(); + usingHextile = False; + if (! appData.encodingsString) { + appDataNew.encodingsString = strdup(prefZRLE); + usingZRLE = True; + fprintf(stderr, "prefer: ZRLE\n"); + } else { + char *t, *z; + static int first = 1; + t = strstr(appData.encodingsString, "tight"); + z = strstr(appData.encodingsString, "zrle"); + if (first && usingZRLE) { + appDataNew.encodingsString = strdup(prefTight); + usingZRLE = False; + usingZYWRLE = False; + } else if (! t) { + appDataNew.encodingsString = strdup(prefZRLE); + usingZRLE = True; + fprintf(stderr, "prefer: ZRLE\n"); + } else if (! z) { + appDataNew.encodingsString = strdup(prefTight); + usingZRLE = False; + usingZYWRLE = False; + skip_maybe_sync = 0; + fprintf(stderr, "prefer: Tight\n"); + } else { + if (t < z) { + appDataNew.encodingsString = strdup(prefZRLE); + usingZRLE = True; + fprintf(stderr, "prefer: ZRLE\n"); + } else { + appDataNew.encodingsString = strdup(prefTight); + usingZRLE = False; + usingZYWRLE = False; + skip_maybe_sync = 0; + fprintf(stderr, "prefer: Tight\n"); + } + } + first = 0; + } + schedule_format_change(); + if (w || ev || params || num_params) {} +} + +void +ToggleZRLEZYWRLE(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + char prefZYWRLE[] = "copyrect zywrle zrle tight zlib hextile corre rre raw"; + char prefZRLE[] = "copyrect zrle zywrle tight zlib hextile corre rre raw"; + init_format_change(); + usingZRLE = True; + usingHextile = False; + if (! appData.encodingsString) { + appDataNew.encodingsString = strdup(prefZYWRLE); + usingZYWRLE = True; + fprintf(stderr, "prefer: ZYWRLE\n"); + } else { + char *z, *w; + w = strstr(appData.encodingsString, "zywrle"); + z = strstr(appData.encodingsString, "zrle"); + if (usingZYWRLE) { + appDataNew.encodingsString = strdup(prefZRLE); + fprintf(stderr, "prefer: ZRLE\n"); + usingZYWRLE = False; + skip_maybe_sync = 0; + } else { + appDataNew.encodingsString = strdup(prefZYWRLE); + fprintf(stderr, "prefer: ZYWRLE\n"); + usingZYWRLE = True; + } + } + schedule_format_change(); + if (w || ev || params || num_params) {} +} + +void +ToggleTightHextile(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + char prefTight[] = "copyrect tight zrle zywrle zlib hextile corre rre raw"; + char prefHextile[] = "copyrect hextile tight zrle zywrle zlib corre rre raw"; + init_format_change(); + usingZRLE = False; + usingZYWRLE = False; + if (! appData.encodingsString) { + appDataNew.encodingsString = strdup(prefHextile); + usingHextile = True; + fprintf(stderr, "prefer: Hextile\n"); + } else { + char *t, *z; + static int first = 1; + t = strstr(appData.encodingsString, "tight"); + z = strstr(appData.encodingsString, "hextile"); + if (first && usingHextile) { + appDataNew.encodingsString = strdup(prefTight); + usingHextile = False; + } else if (! t) { + appDataNew.encodingsString = strdup(prefHextile); + usingHextile = True; + fprintf(stderr, "prefer: Hextile\n"); + } else if (! z) { + appDataNew.encodingsString = strdup(prefTight); + usingHextile = False; + skip_maybe_sync = 0; + fprintf(stderr, "prefer: Tight\n"); + } else { + if (t < z) { + appDataNew.encodingsString = strdup(prefHextile); + usingHextile = True; + fprintf(stderr, "prefer: Hextile\n"); + } else { + appDataNew.encodingsString = strdup(prefTight); + usingHextile = False; + skip_maybe_sync = 0; + fprintf(stderr, "prefer: Tight\n"); + } + } + first = 0; + } + schedule_format_change(); + if (w || ev || params || num_params) {} +} + +void scale_check_zrle(void) { + static int didit = 0; + if (didit) { + return; + } + didit = 1; + if (getenv("SSVNC_PRESERVE_ENCODING")) { + return; + } + if (!usingZRLE && !usingHextile) { + Widget w = 0; + fprintf(stderr, "\nSwitching to faster ZRLE encoding in client-side scaling mode.\n"); + fprintf(stderr, "Switch back to Tight via the Popup menu if you prefer it.\n\n"); + ToggleTightZRLE(w, NULL, NULL, NULL); + } +} + +/* + * ToggleViewOnly + */ + +void +ToggleViewOnly(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.viewOnly) { + appData.viewOnly = False; + fprintf(stderr, "viewonly: off\n"); + } else { + appData.viewOnly = True; + fprintf(stderr, "viewonly: on\n"); + } + Xcursors(1); + if (w || ev || params || num_params) {} +} + +void +ToggleCursorShape(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + init_format_change(); + if (appData.useRemoteCursor) { + appDataNew.useRemoteCursor = False; + fprintf(stderr, "useRemoteCursor: off\n"); + } else { + appDataNew.useRemoteCursor = True; + fprintf(stderr, "useRemoteCursor: on\n"); + } + schedule_format_change(); + if (!appDataNew.useRemoteCursor) { + do_cursor_change = 1; + } else { + do_cursor_change = -1; + } + if (w || ev || params || num_params) {} +} + +void +ToggleCursorAlpha(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.useCursorAlpha) { + appData.useCursorAlpha = False; + fprintf(stderr, "useCursorAlpha: off\n"); + } else { + appData.useCursorAlpha = True; + fprintf(stderr, "useCursorAlpha: on\n"); + } + if (w || ev || params || num_params) {} +} + +void +ToggleX11Cursor(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + init_format_change(); + if (appData.useX11Cursor) { + appDataNew.useX11Cursor = False; + fprintf(stderr, "useX11Cursor: off\n"); + } else { + appDataNew.useX11Cursor = True; + fprintf(stderr, "useX11Cursor: on\n"); + } + schedule_format_change(); + do_cursor_change = 1; + if (w || ev || params || num_params) {} +} + +void +ToggleBell(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.useBell) { + appData.useBell = False; + fprintf(stderr, "useBell: off\n"); + } else { + appData.useBell = True; + fprintf(stderr, "useBell: on\n"); + } + if (w || ev || params || num_params) {} +} + +void +ToggleRawLocal(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + init_format_change(); + if (appData.useRawLocal) { + appDataNew.useRawLocal = False; + fprintf(stderr, "useRawLocal: off\n"); + } else { + appDataNew.useRawLocal = True; + fprintf(stderr, "useRawLocal: on\n"); + } + schedule_format_change(); + if (w || ev || params || num_params) {} +} + +void +ToggleServerInput(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.serverInput) { + appData.serverInput= False; + fprintf(stderr, "serverInput: off\n"); + SendServerInput(True); + } else { + appData.serverInput = True; + fprintf(stderr, "serverInput: on\n"); + SendServerInput(False); + } + if (w || ev || params || num_params) {} +} + +void +TogglePipelineUpdates(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.pipelineUpdates) { + appData.pipelineUpdates= False; + fprintf(stderr, "pipeline-update: off\n"); + } else { + appData.pipelineUpdates = True; + fprintf(stderr, "pipeline-update: on\n"); + } + /* XXX request one to be sure? */ + if (w || ev || params || num_params) {} +} + +void +ToggleSendClipboard(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.sendClipboard) { + appData.sendClipboard= False; + fprintf(stderr, "Send CLIPBOARD Selection: off (send PRIMARY instead)\n"); + } else { + appData.sendClipboard = True; + fprintf(stderr, "Send CLIPBOARD Selection: on (do not send PRIMARY)\n"); + } + if (w || ev || params || num_params) {} +} + +void +ToggleSendAlways(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.sendAlways) { + appData.sendAlways= False; + fprintf(stderr, "Send Selection Always: off\n"); + } else { + appData.sendAlways = True; + fprintf(stderr, "Send Selection Always: on\n"); + } + if (w || ev || params || num_params) {} +} + + +Bool _sw1_ = False; /* XXX this is a weird bug... */ +Bool _sw2_ = False; +Bool _sw3_ = False; +Bool selectingSingleWindow = False; + +extern Cursor bogoCursor; + +void +ToggleSingleWindow(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.singleWindow) { + appData.singleWindow= False; + fprintf(stderr, "singleWindow: off\n"); + SendSingleWindow(-1, -1); + } else { + appData.singleWindow = True; + selectingSingleWindow = True; + fprintf(stderr, "singleWindow: on\n"); + if (bogoCursor != None) { + XDefineCursor(dpy, desktopWin, bogoCursor); + } + } + if (w || ev || params || num_params) {} +} + +void raiseme(int force); +void AppendChatInput(char *); + +extern void ShowChat(Widget w, XEvent *event, String *params, Cardinal *num_params); +extern void ShowFile(Widget w, XEvent *event, String *params, Cardinal *num_params); +extern Bool SendTextChatFinished(void); + + +void printChat(char *str, Bool raise) { + if (appData.termChat) { + if (raise) { + raiseme(0); + } + fprintf(stderr, str); + } else { + if (raise) { + ShowChat(0, 0, 0, 0); + } + AppendChatInput(str); + } +} + +void +ToggleTextChat(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.chatActive) { + printChat("\n*SentClose*\n\n", False); + SendTextChatClose(); + SendTextChatFinished(); + HideChat(0, NULL, NULL, NULL); + appData.chatActive= False; + } else { + ShowChat(0, 0, 0, 0); + SendTextChatOpen(); + if (appData.termChat) { + printChat("\n*SentOpen*\n\nSend: ", True); + } else { + printChat("\n*SentOpen*\n", True); + } + appData.chatActive = True; + } + if (w || ev || params || num_params) {} +} + +extern int filexfer_sock; +extern pid_t java_helper; +#define KILLJAVA +#ifdef KILLJAVA +#include <signal.h> +#endif + +void +ToggleFileXfer(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + static double last_start = 0.0; + if (appData.fileActive) { +#if 0 + HideFile(w, ev, params, num_params); + appData.fileActive = False; +#endif +#ifndef KILLJAVA + if (filexfer_sock >= 0) { + close(filexfer_sock); + } +#else + if (java_helper != 0) { + int i; + if (dnow() < last_start + 6.0) { + fprintf(stderr, "skipping early kill of java helper (less than 5 secs)\n"); + } else { + for (i=1; i<=5; i++) { + pid_t p = java_helper + i; + fprintf(stderr, "trying to kill java helper: %d\n", p); + if (kill(p, SIGTERM) == 0) { + java_helper = 0; + break; + } + } + } + } +#endif + } else { + ShowFile(w, ev, params, num_params); + appData.fileActive = True; + last_start = dnow(); + } + if (w || ev || params || num_params) {} +} + +static int fooHandler(Display *dpy, XErrorEvent *error) { + if (dpy || error) {} + return 0; +} + +void raiseme(int force) { + if ((force || appData.termChat) && getenv("WINDOWID")) { + unsigned long w; + if (sscanf(getenv("WINDOWID"), "%lu", &w) == 1) { + ; + } else if (sscanf(getenv("WINDOWID"), "0x%lx", &w) == 1) { + ; + } else { + w = 0; + } + if (w != 0) { + XErrorHandler old = XSetErrorHandler(fooHandler); + XMapRaised(dpy, (Window) w); + XSync(dpy, False); + XSetErrorHandler(old); + } + } +} + +void set_server_scale(int n) { + if (n >= 1 && n < 100) { + int w = si.framebufferWidth; + int h = si.framebufferHeight; + appData.serverScale = n; + SendServerScale(n); + if (0) SendFramebufferUpdateRequest(0, 0, w, h, False); + schedule_fb_update(); + } +} + +void +DoServerScale(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + char str[100], *s, *q; + int n; + if (1) { + s = DoScaleNDialog(); + } else { + raiseme(1); + fprintf(stderr, "\n\n\a\nEnter integer n for 1/n server scaling: "); + str[0] = '\0'; + fgets(str, 100, stdin); + s = str; + q = strstr(str, "\n"); + if (q) *q = '\0'; + } + if (s[0] != '\0') { + n = atoi(s); + set_server_scale(n); + } + if (w || ev || params || num_params) {} +} + +void set_server_quality(int n) { + fprintf(stderr, "set_quality: %d\n", n); + if (n >= 0 && n <= 9) { + int w = si.framebufferWidth; + int h = si.framebufferHeight; + init_format_change(); + appDataNew.qualityLevel = n; + SendFramebufferUpdateRequest(0, 0, w, h, False); + schedule_format_change(); + } +} + +void +DoServerQuality(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + char str[100], *s, *q; + int n; + if (1) { + s = DoQualityDialog(); + } else { + raiseme(1); + fprintf(stderr, "\n\n\a\nEnter integer 1 <= n <= 9 for quality setting: "); + str[0] = '\0'; + fgets(str, 100, stdin); + s = str; + q = strstr(str, "\n"); + if (q) *q = '\0'; + } + if (s[0] != '\0') { + n = atoi(s); + set_server_quality(n); + } + if (w || ev || params || num_params) {} +} + +void set_server_compress(int n) { + fprintf(stderr, "set_compress: %d\n", n); + if (n >= 0 && n <= 9) { + int w = si.framebufferWidth; + int h = si.framebufferHeight; + init_format_change(); + appDataNew.compressLevel = n; + SendFramebufferUpdateRequest(0, 0, w, h, False); + schedule_format_change(); + } +} + +void +DoServerCompress(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + char str[100], *s, *q; + int n; + if (1) { + s = DoCompressDialog(); + } else { + raiseme(1); + fprintf(stderr, "\n\n\a\nEnter integer 1 <= n <= 9 for compress level setting: "); + str[0] = '\0'; + fgets(str, 100, stdin); + s = str; + q = strstr(str, "\n"); + if (q) *q = '\0'; + } + if (s[0] != '\0') { + n = atoi(s); + set_server_compress(n); + } + if (w || ev || params || num_params) {} +} + +extern void rescale_image(void); +extern void get_scale_values(double *fx, double *fy); + +void +SetScale(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + char *s; + s = DoScaleDialog(); + if (s[0] != '\0') { +#if 0 + int w = si.framebufferWidth; + int h = si.framebufferHeight; +#endif + double fx, fy; + int fs = 0; + if (appData.scale != NULL && !strcmp(s, appData.scale)) { + return; + } + + if (!strcasecmp(s, "none")) { + appData.scale = NULL; + } else if (!strcmp(s, "1.0")) { + appData.scale = NULL; + } else if (!strcmp(s, "1")) { + appData.scale = NULL; + } else { + appData.scale = strdup(s); + } + if (appData.scale != NULL) { + get_scale_values(&fx, &fy); + if (fx <= 0.0 || fy <= 0.0) { + appData.scale = NULL; + return; + } + } + + if (appData.fullScreen) { + fs = 1; + FullScreenOff(); + } + rescale_image(); + if (fs) { + FullScreenOn(); + } + } + if (w || ev || params || num_params) {} +} + +void +SetEscapeKeys(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + char *s; + s = DoEscapeKeysDialog(); + fprintf(stderr, "set escape keys: '%s'\n", s); + if (s[0] != '\0') { + appData.escapeKeys = strdup(s); + } + if (w || ev || params || num_params) {} +} + +void set_ycrop(int n) { + if (n >= 1) { + int w = si.framebufferWidth; + int h = si.framebufferHeight; + appData.yCrop = n; + ReDoDesktop(); + SendFramebufferUpdateRequest(0, 0, w, h, False); + schedule_fb_update(); + } +} - Cleanup(); +void +SetYCrop(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + char str[100], *q, *s; + int n; + if (1) { + s = DoYCropDialog(); + } else { + raiseme(1); + fprintf(stderr, "\n\n\a\nEnter pixel size n -ycrop maximum y-height: "); + str[0] = '\0'; + fgets(str, 100, stdin); + s = str; + q = strstr(str, "\n"); + if (q) *q = '\0'; + } + if (s[0] != '\0') { + n = atoi(s); + set_ycrop(n); + } + if (w || ev || params || num_params) {} +} + +void set_scbar(int n) { + if (n >= 1) { + int w = si.framebufferWidth; + int h = si.framebufferHeight; +fprintf(stderr, "set_scbat: %d\n", n); + appData.sbWidth = n; + ReDoDesktop(); + SendFramebufferUpdateRequest(0, 0, w, h, False); + schedule_fb_update(); + } +} + +void +SetScbar(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + char str[100], *q, *s; + int n; + if (1) { + s = DoScbarDialog(); + } else { + raiseme(1); + fprintf(stderr, "\n\n\a\nEnter pixel size n scrollbar width: "); + str[0] = '\0'; + fgets(str, 100, stdin); + s = str; + q = strstr(str, "\n"); + if (q) *q = '\0'; + } + if (s[0] != '\0') { + n = atoi(s); + set_scbar(n); + } + if (w || ev || params || num_params) {} +} + +void +SetScaleN(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (*num_params != 0) { + int n = atoi(params[0]); + set_server_scale(n); + } + if (w || ev || params || num_params) {} +} + +void UpdateQual(void) { + SetFormatAndEncodings(); + UpdateSubsampButtons(); + UpdateQualSlider(); +} + +extern double latency; + +static void LosslessRefresh(void) { + String encodings = appData.encodingsString; + int compressLevel = appData.compressLevel; + int qual = appData.qualityLevel; + Bool enableJPEG = appData.enableJPEG; + appData.qualityLevel = -1; + appData.enableJPEG = False; + appData.encodingsString = "tight copyrect"; + appData.compressLevel = 1; + SetFormatAndEncodings(); + SendFramebufferUpdateRequest(0, 0, si.framebufferWidth, si.framebufferHeight, False); + if (latency > 0.0) { + if (0) usleep((int) latency * 1000); + } + appData.qualityLevel = qual; + appData.enableJPEG = enableJPEG; + appData.encodingsString = encodings; + appData.compressLevel = compressLevel; + SetFormatAndEncodings(); +} + +static void QualHigh(void) { + appData.encodingsString = "tight copyrect"; + if(appData.useBGR233 || appDataNew.useBGR565) { + fprintf(stderr, "WARNING: Cannot enable JPEG because BGR233/BGR565 is enabled.\n"); + } else { + appData.enableJPEG = True; + } + appData.subsampLevel = TVNC_1X; + appData.qualityLevel = 95; + UpdateQual(); +} + +static void QualMed(void) { + appData.encodingsString = "tight copyrect"; + if(appData.useBGR233 || appDataNew.useBGR565) { + fprintf(stderr, "WARNING: Cannot enable JPEG because BGR233/BGR565 is enabled.\n"); + } else { + appData.enableJPEG = True; + } + appData.subsampLevel = TVNC_2X; + appData.qualityLevel = 80; + UpdateQual(); +} + +static void QualLow(void) { + appData.encodingsString = "tight copyrect"; + if(appData.useBGR233 || appDataNew.useBGR565) { + fprintf(stderr, "WARNING: Cannot enable JPEG because BGR233/BGR565 is enabled.\n"); + } else { + appData.enableJPEG = True; + } + appData.subsampLevel = TVNC_4X; + appData.qualityLevel = 30; + UpdateQual(); +} + +static void QualLossless(void) { + appData.encodingsString = "tight copyrect"; + appData.enableJPEG = False; + appData.compressLevel = 0; + UpdateQual(); +} - return 0; +static void QualLosslessWAN(void) { + appData.encodingsString = "tight copyrect"; + appData.enableJPEG = False; + appData.compressLevel = 1; + UpdateQual(); +} + +void +SetTurboVNC(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (*num_params != 0) { + int n = atoi(params[0]); + if (0) fprintf(stderr, "SetTurboVNC: %d\n", n); + if (n == 1) { + QualHigh(); + } else if (n == 2) { + QualMed(); + } else if (n == 3) { + QualLow(); + } else if (n == 4) { + QualLossless(); + } else if (n == 5) { + QualLosslessWAN(); + } else if (n == 6) { + appData.subsampLevel = TVNC_1X; + UpdateQual(); + } else if (n == 7) { + appData.subsampLevel = TVNC_2X; + UpdateQual(); + } else if (n == 8) { + appData.subsampLevel = TVNC_4X; + UpdateQual(); + } else if (n == 9) { + appData.subsampLevel = TVNC_GRAY; + UpdateQual(); + } else if (n == 10) { + LosslessRefresh(); + } + } + if (w || ev || params || num_params) {} +} + +void +SetQuality(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (*num_params != 0) { + int n = atoi(params[0]); + set_server_quality(n); + } + if (w || ev || params || num_params) {} +} + +void +SetCompress(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (*num_params != 0) { + int n = atoi(params[0]); + set_server_compress(n); + } + if (w || ev || params || num_params) {} +} + +void +GotChatText(char *str, int len) +{ + static char *b = NULL; + static int blen = -1; + int i, k; + if (appData.termChat) { + printChat("\nChat: ", True); + } else { + printChat("Chat: ", True); + } + + if (len < 0) len = 0; + + if (blen < len+1) { + if (b) free(b); + blen = 2 * (len + 10); + b = (char *) malloc(blen); + } + + k = 0; + for (i=0; i < len; i++) { + if (str[i] != '\r') { + b[k++] = str[i]; + } + } + b[k] = '\0'; + b[len] = '\0'; + printChat(b, True); + + if (appData.termChat) { + if (strstr(str, "\n")) { + printChat("Send: ", True); + } else { + printChat("\nSend: ", True); + } + } +} + +void +SetViewOnlyState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.viewOnly) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetNOJPEGState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.enableJPEG) { + XtVaSetValues(w, XtNstate, False, NULL); + } else { + XtVaSetValues(w, XtNstate, True, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetQualityState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (*num_params != 0) { + int n = atoi(params[0]); + if (appData.qualityLevel == n) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + } + if (w || ev || params || num_params) {} +} + +void +SetCompressState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (*num_params != 0) { + int n = atoi(params[0]); + if (appData.compressLevel == n) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + } + if (w || ev || params || num_params) {} +} + +void +SetScaleNState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (*num_params != 0) { + int n = atoi(params[0]); + if (appData.serverScale == n || (appData.serverScale >= 6 && n >= 6)) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + } + if (w || ev || params || num_params) {} +} + +void +Set8bppState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.useBGR233) { + XtVaSetValues(w, XtNstate, True, NULL); + if (b16 != NULL) { + XtVaSetValues(b16, XtNstate, False, NULL); + } + if (bfull != NULL) { + XtVaSetValues(bfull, XtNstate, False, NULL); + } + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +Set16bppState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.useBGR565) { + XtVaSetValues(w, XtNstate, True, NULL); + if (b8 != NULL) { + XtVaSetValues(b8, XtNstate, False, NULL); + } + if (bfull != NULL) { + XtVaSetValues(bfull, XtNstate, False, NULL); + } + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetFullColorState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.useBGR565 || appData.useBGR233) { + XtVaSetValues(w, XtNstate, False, NULL); + } else { + XtVaSetValues(w, XtNstate, True, NULL); + if (b8 != NULL) { + XtVaSetValues(b8, XtNstate, False, NULL); + } + if (b16 != NULL) { + XtVaSetValues(b16, XtNstate, False, NULL); + } + } + if (w || ev || params || num_params) {} +} + +void +SetXGrabState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.grabAll) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetEscapeKeysState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.escapeActive) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +Set256ColorsState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.useBGR233 == 256) { + XtVaSetValues(w, XtNstate, True, NULL); + if (w64 != NULL) { + XtVaSetValues(w64 , XtNstate, False, NULL); + } + if (w8 != NULL) { + XtVaSetValues(w8 , XtNstate, False, NULL); + } + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +Set64ColorsState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.useBGR233 == 64) { + XtVaSetValues(w, XtNstate, True, NULL); + if (w256 != NULL) { + XtVaSetValues(w256, XtNstate, False, NULL); + } + if (w8 != NULL) { + XtVaSetValues(w8 , XtNstate, False, NULL); + } + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +Set8ColorsState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.useBGR233 == 8) { + XtVaSetValues(w, XtNstate, True, NULL); + if (w256 != NULL) { + XtVaSetValues(w256, XtNstate, False, NULL); + } + if (w64 != NULL) { + XtVaSetValues(w64 , XtNstate, False, NULL); + } + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetGreyScaleState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.useGreyScale) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +static void init_state(void) { + static int first = 1; + if (first && appData.encodingsString) { + char *t, *z, *y, *h; + char *str = appData.encodingsString; + int len = strlen(str); + + t = strstr(str, "tight"); + z = strstr(str, "zrle"); + y = strstr(str, "zywrle"); + h = strstr(str, "hextile"); + + if (!t) t = str + len; + if (!z) z = str + len; + if (!y) y = str + len; + if (!h) h = str + len; + + usingZRLE = False; + usingZYWRLE = False; + usingHextile = False; + + if (t < z && t < y && t < h) { + ; + } else if (z < t && z < y && z < h) { + usingZRLE = True; + } else if (y < t && y < z && y < h) { + usingZYWRLE = True; + usingZRLE = True; + } else if (h < t && h < z && h < y) { + usingHextile = True; + } + } + first = 0; + +} + +void +SetZRLEState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + init_state(); + if (usingZRLE) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetHextileState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + init_state(); + if (usingHextile) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetZYWRLEState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + init_state(); + if (usingZYWRLE) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetCursorShapeState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.useRemoteCursor) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetCursorAlphaState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.useCursorAlpha) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetX11CursorState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.useX11Cursor) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetBellState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.useBell) { + XtVaSetValues(w, XtNstate, False, NULL); + } else { + XtVaSetValues(w, XtNstate, True, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetRawLocalState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.useRawLocal) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetServerInputState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (!appData.serverInput) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetPipelineUpdates(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (!appData.pipelineUpdates) { + XtVaSetValues(w, XtNstate, False, NULL); + } else { + XtVaSetValues(w, XtNstate, True, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetSendClipboard(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (!appData.sendClipboard) { + XtVaSetValues(w, XtNstate, False, NULL); + } else { + XtVaSetValues(w, XtNstate, True, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetSendAlways(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (!appData.sendAlways) { + XtVaSetValues(w, XtNstate, False, NULL); + } else { + XtVaSetValues(w, XtNstate, True, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetSingleWindowState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.singleWindow) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetTextChatState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.chatActive) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} +} + +void +SetFileXferState(Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + if (appData.fileActive) { + XtVaSetValues(w, XtNstate, True, NULL); + } else { + XtVaSetValues(w, XtNstate, False, NULL); + } + if (w || ev || params || num_params) {} } diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/vncviewer.h vnc_unixsrc/vncviewer/vncviewer.h --- vnc_unixsrc.orig/vncviewer/vncviewer.h 2004-03-11 13:14:40.000000000 -0500 +++ vnc_unixsrc/vncviewer/vncviewer.h 2010-04-17 22:29:42.000000000 -0400 @@ -28,6 +28,7 @@ #include <string.h> #include <sys/time.h> #include <sys/types.h> +#include <sys/stat.h> #include <unistd.h> #include <pwd.h> #include <X11/IntrinsicP.h> @@ -51,7 +52,13 @@ (((l) & 0x0000ff00) << 8) | \ (((l) & 0x000000ff) << 24)) : (l)) -#define MAX_ENCODINGS 20 +#define Swap32IfBE(l) \ + (*(char *)&endianTest ? (l) : ((((l) & 0xff000000) >> 24) | \ + (((l) & 0x00ff0000) >> 8) | \ + (((l) & 0x0000ff00) << 8) | \ + (((l) & 0x000000ff) << 24)) ) + +#define MAX_ENCODINGS 24 #define FLASH_PORT_OFFSET 5400 #define LISTEN_PORT_OFFSET 5500 @@ -64,60 +71,133 @@ #define DEFAULT_VIA_CMD \ (DEFAULT_SSH_CMD " -f -L %L:%H:%R %G sleep 20") +#define TVNC_SAMPOPT 4 +enum {TVNC_1X=0, TVNC_4X, TVNC_2X, TVNC_GRAY}; -/* argsresources.c */ - -typedef struct { - Bool shareDesktop; - Bool viewOnly; - Bool fullScreen; - Bool grabKeyboard; - Bool raiseOnBeep; - - String encodingsString; - - Bool useBGR233; - int nColours; - Bool useSharedColours; - Bool forceOwnCmap; - Bool forceTrueColour; - int requestedDepth; - - Bool useShm; - - int wmDecorationWidth; - int wmDecorationHeight; - - char *userLogin; - - char *passwordFile; - Bool passwordDialog; - - int rawDelay; - int copyRectDelay; +#if 0 +static const char *subsampLevel2str[TVNC_SAMPOPT] = { + "1X", "4X", "2X", "Gray" +}; +#endif +#ifdef TURBOVNC +#define rfbTightNoZlib 0x0A +#define rfbTurboVncVendor "TRBO" +#define rfbJpegQualityLevel1 0xFFFFFE01 +#define rfbJpegQualityLevel100 0xFFFFFE64 +#define rfbJpegSubsamp1X 0xFFFFFD00 +#define rfbJpegSubsamp4X 0xFFFFFD01 +#define rfbJpegSubsamp2X 0xFFFFFD02 +#define rfbJpegSubsampGray 0xFFFFFD03 +#endif + +/* for debugging width, height, etc */ +#if 0 +#define XtVaSetValues printf("%s:%d\n", __FILE__, __LINE__); XtVaSetValues +#endif - Bool debug; - int popupButtonCount; +/* argsresources.c */ - int bumpScrollTime; - int bumpScrollPixels; +typedef struct { + Bool shareDesktop; + Bool viewOnly; + Bool fullScreen; + Bool grabKeyboard; + Bool raiseOnBeep; + + String encodingsString; + + int useBGR233; + int nColours; + Bool useSharedColours; + Bool forceOwnCmap; + Bool forceTrueColour; + int requestedDepth; + Bool useBGR565; + Bool useGreyScale; + + Bool grabAll; + Bool useXserverBackingStore; + Bool overrideRedir; + Bool popupFix; + + Bool useShm; + Bool termChat; + + int wmDecorationWidth; + int wmDecorationHeight; + + char *userLogin; + char *unixPW; + char *msLogon; + char *repeaterUltra; + Bool ultraDSM; + Bool acceptPopup; + char *rfbVersion; + + char *passwordFile; + Bool passwordDialog; + Bool notty; + + int rawDelay; + int copyRectDelay; + + int yCrop; + int sbWidth; + Bool useCursorAlpha; + Bool useRawLocal; + + Bool debug; + + int popupButtonCount; + int popupButtonBreak; + + int bumpScrollTime; + int bumpScrollPixels; + + int compressLevel; + int qualityLevel; + Bool enableJPEG; + Bool useRemoteCursor; + Bool useX11Cursor; + Bool useBell; + Bool autoPass; + + Bool serverInput; + Bool singleWindow; + int serverScale; + Bool chatActive; + Bool chatOnly; + Bool fileActive; + + char *scale; + char *escapeKeys; + Bool appShare; + Bool escapeActive; + Bool pipelineUpdates; + + Bool sendClipboard; + Bool sendAlways; + char *recvText; + + /* only for turbovnc mode */ + String subsampString; + int subsampLevel; + Bool doubleBuffer; - int compressLevel; - int qualityLevel; - Bool enableJPEG; - Bool useRemoteCursor; - Bool useX11Cursor; - Bool autoPass; + Bool noipv4; + Bool noipv6; } AppData; extern AppData appData; +extern AppData appDataNew; extern char *fallback_resources[]; extern char vncServerHost[]; extern int vncServerPort; extern Bool listenSpecified; +extern pid_t listenParent; extern int listenPort, flashPort; extern XrmOptionDescRec cmdLineOptions[]; @@ -130,10 +210,11 @@ /* colour.c */ extern unsigned long BGR233ToPixel[]; +extern unsigned long BGR565ToPixel[]; extern Colormap cmap; extern Visual *vis; -extern unsigned int visdepth, visbpp; +extern unsigned int visdepth, visbpp, isLSB; extern void SetVisualAndCmap(); @@ -155,15 +236,60 @@ extern GC srcGC, dstGC; extern Dimension dpyWidth, dpyHeight; +extern int appshare_0_hint; +extern int appshare_x_hint; +extern int appshare_y_hint; + extern void DesktopInitBeforeRealization(); extern void DesktopInitAfterRealization(); +extern void Xcursors(int set); extern void SendRFBEvent(Widget w, XEvent *event, String *params, Cardinal *num_params); extern void CopyDataToScreen(char *buf, int x, int y, int width, int height); +extern void FillScreen(int x, int y, int width, int height, unsigned long fill); extern void SynchroniseScreen(); +extern void ReDoDesktop(); +extern void DesktopCursorOff(); +extern void put_image(int x1, int y1, int x2, int y2, int width, int height, int solid); +extern void copy_rect(int x, int y, int width, int height, int src_x, int src_y); + +extern void releaseAllPressedModifiers(void); +extern void fs_grab(int check); +extern void fs_ungrab(int check); + /* dialogs.c */ +extern int use_tty(void); + +extern void ScaleDialogDone(Widget w, XEvent *event, String *params, + Cardinal *num_params); +extern char *DoScaleDialog(); + +extern void EscapeDialogDone(Widget w, XEvent *event, String *params, + Cardinal *num_params); +extern char *DoEscapeKeysDialog(); + +extern void YCropDialogDone(Widget w, XEvent *event, String *params, + Cardinal *num_params); +extern char *DoYCropDialog(); + +extern void ScbarDialogDone(Widget w, XEvent *event, String *params, + Cardinal *num_params); +extern char *DoScbarDialog(); + +extern void ScaleNDialogDone(Widget w, XEvent *event, String *params, + Cardinal *num_params); +extern char *DoScaleNDialog(); + +extern void QualityDialogDone(Widget w, XEvent *event, String *params, + Cardinal *num_params); +extern char *DoQualityDialog(); + +extern void CompressDialogDone(Widget w, XEvent *event, String *params, + Cardinal *num_params); +extern char *DoCompressDialog(); + extern void ServerDialogDone(Widget w, XEvent *event, String *params, Cardinal *num_params); extern char *DoServerDialog(); @@ -171,6 +297,10 @@ Cardinal *num_params); extern char *DoPasswordDialog(); +extern void UserDialogDone(Widget w, XEvent *event, String *params, + Cardinal *num_params); +extern char *DoUserDialog(); + /* fullscreen.c */ extern void ToggleFullScreen(Widget w, XEvent *event, String *params, @@ -181,6 +311,13 @@ extern void FullScreenOn(); extern void FullScreenOff(); +extern int net_wm_supported(void); + +extern void JumpLeft(Widget w, XEvent *event, String *params, Cardinal *num_params); +extern void JumpRight(Widget w, XEvent *event, String *params, Cardinal *num_params); +extern void JumpUp(Widget w, XEvent *event, String *params, Cardinal *num_params); +extern void JumpDown(Widget w, XEvent *event, String *params, Cardinal *num_params); + /* listen.c */ extern void listenForIncomingConnections(); @@ -196,6 +333,8 @@ Cardinal *num_params); extern void Quit(Widget w, XEvent *event, String *params, Cardinal *num_params); +extern void HideChat(Widget w, XEvent *event, String *params, + Cardinal *num_params); extern void Cleanup(); /* popup.c */ @@ -207,6 +346,29 @@ Cardinal *num_params); extern void CreatePopup(); +extern void HideScaleN(Widget w, XEvent *event, String *params, + Cardinal *num_params); +extern void CreateScaleN(); + +extern void HideTurboVNC(Widget w, XEvent *event, String *params, + Cardinal *num_params); +extern void CreateTurboVNC(); +extern void UpdateSubsampButtons(); +extern void UpdateQualSlider(); +extern void UpdateQual(); + +extern void HideQuality(Widget w, XEvent *event, String *params, + Cardinal *num_params); +extern void CreateQuality(); + +extern void HideCompress(Widget w, XEvent *event, String *params, + Cardinal *num_params); +extern void CreateCompress(); + +extern void Noop(Widget w, XEvent *event, String *params, + Cardinal *num_params); + +extern int CreateMsg(char *msg, int wait); /* rfbproto.c */ extern int rfbsock; @@ -229,8 +391,19 @@ extern Bool SendClientCutText(char *str, int len); extern Bool HandleRFBServerMessage(); +extern Bool SendServerInput(Bool enabled); +extern Bool SendSingleWindow(int x, int y); +extern Bool SendServerScale(int n); + +extern Bool SendTextChat(char *str); +extern Bool SendTextChatOpen(void); +extern Bool SendTextChatClose(void); +extern Bool SendTextChatFinish(void); + extern void PrintPixelFormat(rfbPixelFormat *format); +extern double dnow(void); + /* selection.c */ extern void InitialiseSelection(); @@ -241,8 +414,10 @@ /* shm.c */ -extern XImage *CreateShmImage(); +extern XImage *CreateShmImage(int do_ycrop); extern void ShmCleanup(); +extern void ShmDetach(); +extern Bool UsingShm(); /* sockets.c */ @@ -252,11 +427,19 @@ extern Bool WriteExact(int sock, char *buf, int n); extern int FindFreeTcpPort(void); extern int ListenAtTcpPort(int port); -extern int ConnectToTcpAddr(unsigned int host, int port); +extern int ListenAtTcpPort6(int port); +extern int dotted_ip(char *host, int partial); +extern int ConnectToTcpAddr(const char *hostname, int port); +extern int ConnectToUnixSocket(char *file); extern int AcceptTcpConnection(int listenSock); +extern int AcceptTcpConnection6(int listenSock); extern Bool SetNonBlocking(int sock); +extern Bool SetNoDelay(int sock); +extern Bool SocketPair(int fd[2]); extern int StringToIPAddr(const char *str, unsigned int *addr); +extern char *get_peer_ip(int sock); +extern char *ip2host(char *ip); extern Bool SameMachine(int sock); /* tunnel.c */ @@ -271,3 +454,82 @@ extern XtAppContext appContext; extern Display* dpy; extern Widget toplevel; + +extern void GotChatText(char *str, int len); +extern void unixpw(char *instr, int vencrypt_plain); + +extern void Toggle8bpp(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void Toggle16bpp(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleFullColor(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void Toggle256Colors(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void Toggle64Colors(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void Toggle8Colors(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleGreyScale(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleTightZRLE(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleTightHextile(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleZRLEZYWRLE(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleViewOnly(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleJPEG(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleCursorShape(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleCursorAlpha(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleX11Cursor(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleBell(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleRawLocal(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleServerInput(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void TogglePipelineUpdates(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleSendClipboard(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleSendAlways(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleSingleWindow(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleXGrab(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleEscapeActive(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetEscapeKeys(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void DoServerScale(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void DoServerQuality(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void DoServerCompress(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetScale(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetYCrop(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetScbar(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ShowScaleN(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ShowTurboVNC(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ShowQuality(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ShowCompress(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetScaleN(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetTurboVNC(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetQuality(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetCompress(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleTextChat(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleFileXfer(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void ToggleTermTextChat(Widget w, XEvent *ev, String *params, Cardinal *num_params); + +extern void scale_check_zrle(void); + +extern void SetViewOnlyState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetNOJPEGState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetScaleNState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetQualityState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetCompressState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void Set8bppState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void Set16bppState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetFullColorState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void Set256ColorsState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void Set64ColorsState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void Set8ColorsState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetGreyScaleState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetZRLEState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetHextileState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetZYWRLEState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetCursorShapeState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetCursorAlphaState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetX11CursorState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetBellState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetRawLocalState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetServerInputState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetPipelineUpdates(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetSendClipboard(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetSendAlways(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetSingleWindowState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetTextChatState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetTermTextChatState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetFileXferState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetXGrabState(Widget w, XEvent *ev, String *params, Cardinal *num_params); +extern void SetEscapeKeysState(Widget w, XEvent *ev, String *params, Cardinal *num_params); diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/vncviewer.man vnc_unixsrc/vncviewer/vncviewer.man --- vnc_unixsrc.orig/vncviewer/vncviewer.man 2004-03-11 13:14:40.000000000 -0500 +++ vnc_unixsrc/vncviewer/vncviewer.man 2010-04-11 23:30:24.000000000 -0400 @@ -5,38 +5,55 @@ .\" Copyright (C) 1998 Marcus.Brinkmann@ruhr-uni-bochum.de .\" Copyright (C) 2000,2001 Red Hat, Inc. .\" Copyright (C) 2001-2003 Constantin Kaplinsky <const@ce.cctpu.edu.ru> +.\" Copyright (C) 2006-2010 Karl J. Runge <runge@karlrunge.com> .\" .\" You may distribute under the terms of the GNU General Public .\" License as specified in the file LICENCE.TXT that comes with the .\" TightVNC distribution. .\" -.TH vncviewer 1 "January 2003" "" "TightVNC" +.TH ssvncviewer 1 "April 2010" "" "SSVNC" .SH NAME -vncviewer \- an X viewer client for VNC +ssvncviewer \- an X viewer client for VNC .SH SYNOPSIS -.B vncviewer +.B ssvncviewer .RI [\| options \|] .RI [\| host \|][\| :display \|] .br -.B vncviewer +.B ssvncviewer .RI [\| options \|] .RI [\| host \|][\| ::port \|] .br -.B vncviewer +.B ssvncviewer +.RI [\| options \|] +.RI exec=[\| cmd+args... \|] +.br +.B ssvncviewer +.RI [\| options \|] +.RI fd=n +.br +.B ssvncviewer +.RI [\| options \|] +.RI /path/to/unix/socket +.br +.B ssvncviewer .RI [\| options \|] .IR \-listen .RI [\| display \|] .br -.B vncviewer +.B ssvncviewer .IR \-help .br .SH DESCRIPTION -.B vncviewer +.B ssvncviewer is an Xt\-based client application for the VNC (Virtual Network Computing) system. It can connect to any VNC\-compatible server such -as \fBXvnc\fR or WinVNC, allowing you to control desktop environment +as \fBXvnc\fR, WinVNC, or \fBx11vnc\fR, allowing you to control desktop environment of a different machine. +ssvncviewer is an enhanced version of the tightvnc unix viewer that can +take advantage of features in the \fBx11vnc\fR and UltraVNC VNC servers. +See below for the description of these features. + You can use F8 to display a pop\-up utility menu. Press F8 twice to pass single F8 to the remote side. .SH OPTIONS @@ -102,13 +119,13 @@ TightVNC supports several different compression methods to encode screen updates; this option specifies a set of them to use in order of preference. Encodings are specified separated with spaces, and must -thus be enclosed in quotes if more than one is specified. Available -encodings, in default order for a remote connection, are "copyrect -tight hextile zlib corre rre raw". For a local connection (to the same -machine), the default order to try is "raw copyrect tight hextile zlib -corre rre". Raw encoding is always assumed as a last option if no -other encoding can be used for some reason. For more information on -encodings, see the section ENCODINGS below. +thus be enclosed in quotes if more than one is specified. Commas may be used to avoid spaces. +Available encodings, in default order for a remote connection, are +"copyrect tight hextile zlib corre rre raw". For a local connection +(to the same machine), the default order to try is "raw copyrect tight +hextile zlib corre rre". Raw encoding is always assumed as a last option +if no other encoding can be used for some reason. For more information +on encodings, see the section ENCODINGS below. .TP \fB\-bgr233\fR Always use the BGR233 format to encode pixel data. This reduces @@ -168,6 +185,424 @@ \fB\-autopass\fR Read a plain-text password from stdin. This option affects only the standard VNC authentication. + +.SH Enhanced TightVNC Viewer (SSVNC) OPTIONS +.TP +Enhanced TightVNC Viewer (SSVNC) web page is located at: +.TP +http://www.karlrunge.com/x11vnc/ssvnc.html +.TP +Note: ZRLE and ZYWRLE encodings are now supported. +.TP +Note: F9 is shortcut to Toggle FullScreen mode. +.TP +Note: In -listen mode set the env var. SSVNC_MULTIPLE_LISTEN=1 +to allow more than one incoming VNC server at a time. +This is the same as -multilisten described below. Set +SSVNC_MULTIPLE_LISTEN=MAX:n to allow no more than "n" +simultaneous reverse connections. + +If the host:port is specified as "exec=command args..." +then instead of making a TCP/IP socket connection to the +remote VNC server, "command args..." is executed and the +viewer is attached to its stdio. This enables tunnelling +established via an external command, e.g. an stunnel(8) +that does not involve a listening socket. +This mode does not work for -listen reverse connections. + +If the host:port is specified as "fd=n" then it is assumed +n is an already opened file descriptor to the socket. (i.e +the parent did fork+exec) + +If the host:port contains a '/' it is interpreted as a +unix-domain socket (AF_LOCAL insead of AF_INET) +.TP +\fB\-multilisten\fR +As in -listen (reverse connection listening) except +allow more than one incoming VNC server to be connected +at a time. The default for -listen of only one at a +time tries to play it safe by not allowing anyone on +the network to put (many) desktops on your screen over +a long window of time. Use -multilisten for no limit. +.TP +\fB\-acceptpopup\fR +In \fB\-listen\fR (reverse connection listening) mode when +a reverse VNC connection comes in show a popup asking +whether to Accept or Reject the connection. The IP +address of the connecting host is shown. Same as +setting the env. var. SSVNC_ACCEPT_POPUP=1. +.TP +\fB\-acceptpopupsc\fR +As in \fB\-acceptpopup\fR except assume UltraVNC Single +Click (SC) server. Retrieve User and ComputerName +info from UltraVNC Server and display in the Popup. +.TP +\fB\-use64\fR +In \fB\-bgr233\fR mode, use 64 colors instead of 256. +.TP +\fB\-bgr222\fR +Same as \fB\-use64\fR. +.TP +\fB\-use8\fR +In \fB\-bgr233\fR mode, use 8 colors instead of 256. +.TP +\fB\-bgr111\fR +Same as \fB\-use8\fR. +.TP +\fB\-16bpp\fR +If the vnc viewer X display is depth 24 at 32bpp +request a 16bpp format from the VNC server to cut +network traffic by up to 2X, then tranlate the +pixels to 32bpp locally. +.TP +\fB\-bgr565\fR +Same as \fB\-16bpp\fR. +.TP +\fB\-grey\fR +Use a grey scale for the 16- and 8\fB\-bpp\fR modes. +.TP +\fB\-alpha\fR +Use alphablending transparency for local cursors +requires: x11vnc server, both client and server +must be 32bpp and same endianness. +.TP +\fB\-scale\fR \fIstr\fR +Scale the desktop locally. The string "str" can +a floating point ratio, e.g. "0.9", or a fraction, +e.g. "3/4", or WxH, e.g. 1280x1024. Use "fit" +to fit in the current screen size. Use "auto" to +fit in the window size. "str" can also be set by +the env. var. SSVNC_SCALE. + +If you observe mouse trail painting errors, enable +X11 Cursor mode (either via Popup or \fB\-x11cursor\fR.) + +Note that scaling is done in software and so can be +slow and requires more memory. Some speedup Tips: + +ZRLE is faster than Tight in this mode. When +scaling is first detected, the encoding will +be automatically switched to ZRLE. Use the +Popup menu if you want to go back to Tight. +Set SSVNC_PRESERVE_ENCODING=1 to disable this. + +Use a solid background on the remote side. +(e.g. manually or via x11vnc \fB\-solid\fR ...) + +If the remote server is x11vnc, try client +side caching: x11vnc \fB\-ncache\fR 10 ... +.TP +\fB\-ycrop\fR n +Only show the top n rows of the framebuffer. For +use with x11vnc \fB\-ncache\fR client caching option +to help "hide" the pixel cache region. +Use a negative value (e.g. \fB\-1\fR) for autodetection. +Autodetection will always take place if the remote +fb height is more than 2 times the width. +.TP +\fB\-sbwidth\fR n +Scrollbar width for x11vnc \fB\-ncache\fR mode (\fB\-ycrop\fR), +default is very narrow: 2 pixels, it is narrow to +avoid distraction in \fB\-ycrop\fR mode. +.TP +\fB\-nobell\fR +Disable bell. +.TP +\fB\-rawlocal\fR +Prefer raw encoding for localhost, default is +no, i.e. assumes you have a SSH tunnel instead. +.TP +\fB\-notty\fR +Try to avoid using the terminal for interactive +responses: use windows for messages and prompting +instead. Messages will also be printed to terminal. +.TP +\fB\-sendclipboard\fR +Send the X CLIPBOARD selection (i.e. Ctrl+C, +Ctrl+V) instead of the X PRIMARY selection (mouse +select and middle button paste.) +.TP +\fB\-sendalways\fR +Whenever the mouse enters the VNC viewer main +window, send the selection to the VNC server even if +it has not changed. This is like the Xt resource +translation SelectionToVNC(always) +.TP +\fB\-recvtext\fR +str When cut text is received from the VNC server, +ssvncviewer will set both the X PRIMARY and the +X CLIPBOARD local selections. To control which +is set, specify 'str' as 'primary', 'clipboard', +or 'both' (the default.) +.TP +\fB\-graball\fR +Grab the entire X server when in fullscreen mode, +needed by some old window managers like fvwm2. +.TP +\fB\-popupfix\fR +Warp the popup back to the pointer position, +needed by some old window managers like fvwm2. +.TP +\fB\-grabkbd\fR +Grab the X keyboard when in fullscreen mode, +needed by some window managers. Same as \fB\-grabkeyboard\fR. +\fB\-grabkbd\fR is the default, use \fB\-nograbkbd\fR to disable. +.TP +\fB\-bs\fR, \fB\-nobs\fR +Whether or not to use X server Backingstore for the +main viewer window. The default is to not, mainly +because most Linux, etc, systems X servers disable +*all* Backingstore by default. To re\fB\-enable\fR it put + +Option "Backingstore" + +in the Device section of /etc/X11/xorg.conf. +In \fB\-bs\fR mode with no X server backingstore, whenever an +area of the screen is re\fB\-exposed\fR it must go out to the +VNC server to retrieve the pixels. This is too slow. + +In \fB\-nobs\fR mode, memory is allocated by the viewer to +provide its own backing of the main viewer window. This +actually makes some activities faster (changes in large +regions) but can appear to "flash" too much. +.TP +\fB\-noshm\fR +Disable use of MIT shared memory extension (not recommended) +.TP +\fB\-termchat\fR +Do the UltraVNC chat in the terminal vncviewer is in +instead of in an independent window. +.TP +\fB\-unixpw\fR \fIstr\fR +Useful for logging into x11vnc in \fB\-unixpw\fR mode. "str" is a +string that allows many ways to enter the Unix Username +and Unix Password. These characters: username, newline, +password, newline are sent to the VNC server after any VNC +authentication has taken place. Under x11vnc they are +used for the \fB\-unixpw\fR login. Other VNC servers could do +something similar. + +You can also indicate "str" via the environment +variable SSVNC_UNIXPW. + +Note that the Escape key is actually sent first to tell +x11vnc to not echo the Unix Username back to the VNC +viewer. Set SSVNC_UNIXPW_NOESC=1 to override this. + +If str is ".", then you are prompted at the command line +for the username and password in the normal way. If str is +"-" the stdin is read via getpass(3) for username@password. +Otherwise if str is a file, it is opened and the first line +read is taken as the Unix username and the 2nd as the +password. If str prefixed by "rm:" the file is removed +after reading. Otherwise, if str has a "@" character, +it is taken as username@password. Otherwise, the program +exits with an error. Got all that? +.TP +\fB-repeater\fR \fIstr\fR +This is for use with UltraVNC repeater proxy described +here: http://www.uvnc.com/addons/repeater.html. The "str" +is the ID string to be sent to the repeater. E.g. ID:1234 +It can also be the hostname and port or display of the VNC +server, e.g. 12.34.56.78:0 or snoopy.com:1. Note that when +using -repeater, the host:dpy on the cmdline is the repeater +server, NOT the VNC server. The repeater will connect you. + +Example: vncviewer ... -repeater ID:3333 repeat.host:5900 + +Example: vncviewer ... -repeater vhost:0 repeat.host:5900 + +Use, e.g., '-repeater SCIII=ID:3210' if the repeater is a +Single Click III (SSL) repeater (repeater_SSL.exe) and you +are passing the SSL part of the connection through stunnel, socat, etc. +This way the magic UltraVNC string 'testB' needed to work with the +repeater is sent to it. +.TP +\fB-rfbversion\fR \fIstr\fR +Set the advertised RFB version. E.g.: -rfbversion 3.6 For some +servers, e.g. UltraVNC this needs to be done. +.TP +\fB-ultradsm\fR +UltraVNC has symmetric private encryption DSM plugins. See +http://www.uvnc.com/features/encryption.html. It is assumed +you are using a unix program (e.g. our ultravnc_dsm_helper) to +encrypt and decrypt the UltraVNC DSM stream. IN ADDITION TO +THAT supply -ultradsm to tell THIS viewer to modify the RFB +data sent so as to work with the UltraVNC Server. For some +reason, each RFB msg type must be sent twice under DSM. +.TP +\fB\-mslogon\fR \fIuser\fR +Use Windows MS Logon to an UltraVNC server. Supply the +username or "1" to be prompted. The default is to +autodetect the UltraVNC MS Logon server and prompt for +the username and password. + +IMPORTANT NOTE: The UltraVNC MS-Logon Diffie-Hellman +exchange is very weak and can be brute forced to recover +your username and password in a few seconds of CPU +time. To be safe, be sure to use an additional encrypted +tunnel (e.g. SSL or SSH) for the entire VNC session. +.TP +\fB\-chatonly\fR +Try to be a client that only does UltraVNC text chat. This +mode is used by x11vnc to present a chat window on the physical +X11 console (i.e. to chat with the person at the display). +.TP +\fB-env\fR \fIVAR=VALUE\fR +To save writing a shell script to set environment +variables, specify as many as you need on the command line. For example, +-env SSVNC_MULTIPLE_LISTEN=MAX:5 -env EDITOR=vi +.TP +\fB\-noipv6\fR +Disable all IPv6 sockets. Same as VNCVIEWER_NO_IPV6=1. +.TP +\fB\-noipv4\fR +Disable all IPv4 sockets. Same as VNCVIEWER_NO_IPV4=1. +.TP +\fB\-printres\fR +Print out the Ssvnc X resources (appdefaults) and +then exit. You can save them to a file and customize them (e.g. the +keybindings and Popup menu) Then point to the file via +XENVIRONMENT or XAPPLRESDIR. +.TP +\fB\-pipeline\fR +Like TurboVNC, request the next framebuffer update as soon +as possible instead of waiting until the end of the current +framebuffer update coming in. Helps 'pipeline' the updates. +This is currently the default, use \fB-nopipeline\fR to disable. +.TP +\fB\-appshare\fR +Enable features for use with x11vnc's \fB\-appshare\fR mode where +instead of sharing the full desktop only the application's +windows are shared. Viewer multilisten mode is used to +create the multiple windows: \fB\-multilisten\fR is implied. +See 'x11vnc \fB\-appshare\fR \fB\-help\fR' more information on the mode. +Features enabled in the viewer under \fB\-appshare\fR are: +Minimum extra text in the title, auto \fB\-ycrop\fR is disabled, +x11vnc \fB\-remote_prefix\fR X11VNC_APPSHARE_CMD: message channel, +x11vnc initial window position hints. See also Escape Keys +below for additional key and mouse bindings. +.TP +\fB\-escape \fR\fIstr\fR +This sets the 'Escape Keys' modifier sequence and enables +escape keys mode. When the modifier keys escape sequence +is held down, the next keystroke is interpreted locally +to perform a special action instead of being sent to the +remote VNC server. + +Use '\fB\-escape\fR default' for the default modifier sequence. +(Unix: Alt_L,Super_L and MacOSX: Control_L,Meta_L) + +Here are the 'Escape Keys: Help+Set' instructions from the Popup: + +Escape Keys: Enter a comma separated list of modifier keys to be the 'escape +sequence'. When these keys are held down, the next keystroke is +interpreted locally to invoke a special action instead of being sent to +the remote VNC server. In other words, a set of 'Hot Keys'. + +Here is the list of local key mappings to special actions: + +r: refresh desktop b: toggle bell c: toggle full-color + +f: file transfer x: x11cursor z: toggle Tight/ZRLE + +l: full screen g: graball e: escape keys dialog + +s: scale dialog +: scale up (=) -: scale down (_) + +t: text chat a: alphablend cursor + +V: toggle viewonly Q: quit viewer 123456: UltraVNC scale 1/n + +Arrow keys: pan the viewport about 10% for each keypress. + +PageUp/PageDown: pan the viewport by a screenful vertically. + +Home/End: pan the viewport by a screenful horizontally. + +KeyPad Arrows: pan the viewport by 1 pixel for each keypress. + +Dragging the Mouse with Button1 pressed also pans the viewport. + +Clicking Mouse Button3 brings up the Popup Menu. + +The above mappings are \fBalways\fR active in ViewOnly mode, unless you set +the Escape Keys value to 'never'. + +x11vnc -appshare hot-keys: x11vnc has a simple application sharing mode +that enables the viewer-side to move, resize, or raise the remote toplevel +windows. To enable it, hold down Shift + the Escape Keys and press these: + +Arrow keys: move the remote window around in its desktop. + +PageUp/PageDn/Home/End: resize the remote window. + ++/-: raise or lower the remote window. + +M or Button1 move win to local position; D or Button3: delete remote win. + +If the Escape Keys value below is set to 'default' then a default list of +of modifier keys is used. For Unix it is: Alt_L,Super_L and for MacOSX it +is Control_L,Meta_L. Note: the Super_L key usually has a Windows(TM) Flag +on it. Also note the _L and _R mean the key is on the LEFT or RIGHT side +of the keyboard. + +On Unix the default is Alt and Windows keys on Left side of keyboard. +On MacOSX the default is Control and Command keys on Left side of keyboard. + +Example: Press and hold the Alt and Windows keys on the LEFT side of the +keyboard and then press 'c' to toggle the full-color state. Or press 't' +to toggle the ultravnc Text Chat window, etc. + +To use something besides the default, supply a comma separated list (or a +single one) from: Shift_L Shift_R Control_L Control_R Alt_L Alt_R Meta_L +Meta_R Super_L Super_R Hyper_L Hyper_R or Mode_switch. +.TP +\fB New Popup actions:\fR + + ViewOnly: ~ -viewonly + Disable Bell: ~ -nobell + Cursor Shape: ~ -nocursorshape + X11 Cursor: ~ -x11cursor + Cursor Alphablend: ~ -alpha + Toggle Tight/Hextile: ~ -encodings hextile... + Toggle Tight/ZRLE: ~ -encodings zrle... + Toggle ZRLE/ZYWRLE: ~ -encodings zywrle... + Quality Level ~ -quality (both Tight and ZYWRLE) + Compress Level ~ -compresslevel + Disable JPEG: ~ -nojpeg (Tight) + Pipeline Updates ~ -pipeline + + Full Color as many colors as local screen allows. + Grey scale (16 & 8-bpp) ~ -grey, for low colors 16/8bpp modes only. + 16 bit color (BGR565) ~ -16bpp / -bgr565 + 8 bit color (BGR233) ~ -bgr233 + 256 colors ~ -bgr233 default # of colors. + 64 colors ~ -bgr222 / -use64 + 8 colors ~ -bgr111 / -use8 + Scale Viewer ~ -scale + Escape Keys: Toggle ~ -escape + Escape Keys: Help+Set ~ -escape + Set Y Crop (y-max) ~ -ycrop + Set Scrollbar Width ~ -sbwidth + XGrabServer ~ -graball + + UltraVNC Extensions: + + Set 1/n Server Scale Ultravnc ext. Scale desktop by 1/n. + Text Chat Ultravnc ext. Do Text Chat. + File Transfer Ultravnc ext. File xfer via Java helper. + Single Window Ultravnc ext. Grab and view a single window. + (select then click on the window you want). + Disable Remote Input Ultravnc ext. Try to prevent input and + viewing of monitor at physical display. + + Note: the Ultravnc extensions only apply to servers that support + them. x11vnc/libvncserver supports some of them. + + Send Clipboard not Primary ~ -sendclipboard + Send Selection Every time ~ -sendalways + .SH ENCODINGS The server supplies information in whatever format is desired by the client, in order to make the client as easy as possible to implement. @@ -238,6 +673,15 @@ \-quality and \-nojpeg options above). Tight encoding is usually the best choice for low\-bandwidth network environments (e.g. slow modem connections). +.TP +.B ZRLE +The SSVNC viewer has ported the RealVNC (www.realvnc.com) ZRLE encoding +to the unix tightvnc viewer. +.TP +.B ZYWRLE +The SSVNC viewer has ported the Hitachi lossy wavelet based ZRLE +encoding from http://mobile.hitachi-system.co.jp/publications/ZYWRLE/ +to the unix tightvnc viewer. .SH RESOURCES X resources that \fBvncviewer\fR knows about, aside from the normal Xt resources, are as follows: @@ -364,12 +808,13 @@ .B %R remote TCP port number. .SH SEE ALSO -\fBvncserver\fR(1), \fBXvnc\fR(1), \fBvncpasswd\fR(1), -\fBvncconnect\fR(1), \fBssh\fR(1) +\fBvncserver\fR(1), \fBx11vnc\fR(1), \fBssvnc\fR(1), \fBXvnc\fR(1), \fBvncpasswd\fR(1), +\fBvncconnect\fR(1), \fBssh\fR(1), http://www.karlrunge.com/x11vnc, http://www.karlrunge.com/x11vnc/ssvnc.html .SH AUTHORS Original VNC was developed in AT&T Laboratories Cambridge. TightVNC additions was implemented by Constantin Kaplinsky. Many other people -participated in development, testing and support. +participated in development, testing and support. Karl J. Runge +added all of the SSVNC related features and improvements. \fBMan page authors:\fR .br @@ -380,3 +825,5 @@ Tim Waugh <twaugh@redhat.com>, .br Constantin Kaplinsky <const@ce.cctpu.edu.ru> +.br +Karl J. Runge <runge@karlrunge.com> diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/zrle.c vnc_unixsrc/vncviewer/zrle.c --- vnc_unixsrc.orig/vncviewer/zrle.c 2007-02-04 18:59:50.000000000 -0500 +++ vnc_unixsrc/vncviewer/zrle.c 2010-02-25 23:24:28.000000000 -0500 @@ -0,0 +1,620 @@ +/* + * Copyright (C) 2005 Johannes E. Schindelin. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * zrle.c - handle zrle encoding. + * + * This file shouldn't be compiled directly. It is included multiple times by + * rfbproto.c, each time with a different definition of the macro BPP. For + * each value of BPP, this file defines a function which handles an zrle + * encoded rectangle with BPP bits per pixel. + */ + +#ifndef REALBPP +#define REALBPP BPP +#endif + +#if !defined(UNCOMP) || UNCOMP==0 +#define HandleZRLE CONCAT2E(HandleZRLE,REALBPP) +#define HandleZRLETile CONCAT2E(HandleZRLETile,REALBPP) +#elif UNCOMP>0 +#define HandleZRLE CONCAT3E(HandleZRLE,REALBPP,Down) +#define HandleZRLETile CONCAT3E(HandleZRLETile,REALBPP,Down) +#else +#define HandleZRLE CONCAT3E(HandleZRLE,REALBPP,Up) +#define HandleZRLETile CONCAT3E(HandleZRLETile,REALBPP,Up) +#endif +#undef CARDBPP +#undef CARDREALBPP +#define CARDBPP CONCAT2E(CARD, BPP) +#define CARDREALBPP CONCAT2E(CARD,REALBPP) + +#define FillRectangle(x, y, w, h, color) \ + { \ + XGCValues _gcv; \ + _gcv.foreground = color; \ + if (!appData.useXserverBackingStore) { \ + FillScreen(x, y, w, h, _gcv.foreground); \ + } else { \ + XChangeGC(dpy, gc, GCForeground, &_gcv); \ + XFillRectangle(dpy, desktopWin, gc, x, y, w, h); \ + } \ + } + +#if defined(__sparc) || defined(__sparc__) || defined(__ppc__) || defined(__POWERPC__) || defined(__BIG_ENDIAN__) || defined(_BIG_ENDIAN) +#define IS_BIG_ENDIAN 1 +#else +#define IS_BIG_ENDIAN 0 +#endif + +#if DO_ZYWRLE + +#define ENDIAN_LITTLE 0 +#define ENDIAN_BIG 1 +#define ENDIAN_NO 2 +#if IS_BIG_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_BIG +#else +#define ZYWRLE_ENDIAN ENDIAN_LITTLE +#endif +#undef END_FIX +#if ZYWRLE_ENDIAN == ENDIAN_LITTLE +# define END_FIX LE +#elif ZYWRLE_ENDIAN == ENDIAN_BIG +# define END_FIX BE +#else +# define END_FIX NE +#endif +#define __RFB_CONCAT3E(a,b,c) CONCAT3E(a,b,c) +#define __RFB_CONCAT2E(a,b) CONCAT2E(a,b) +#undef CPIXEL +#if REALBPP != BPP +#if UNCOMP == 0 +#define CPIXEL REALBPP +#elif UNCOMP>0 +#define CPIXEL CONCAT2E(REALBPP,Down) +#else +#define CPIXEL CONCAT2E(REALBPP,Up) +#endif +#endif +#define PIXEL_T CARDBPP +#if BPP!=8 +#define ZYWRLE_DECODE 1 +#include "zywrletemplate.c" +#endif +#undef CPIXEL + +#endif /* DO_ZYWRLE */ + +static int HandleZRLETile( + unsigned char* buffer,size_t buffer_length, + int x,int y,int w,int h); + +static Bool +HandleZRLE (int rx, int ry, int rw, int rh) +{ + rfbZRLEHeader header; + int remaining; + int inflateResult; + int toRead; + int min_buffer_size = rw * rh * (REALBPP / 8) * 2; + + /* First make sure we have a large enough raw buffer to hold the + * decompressed data. In practice, with a fixed REALBPP, fixed frame + * buffer size and the first update containing the entire frame + * buffer, this buffer allocation should only happen once, on the + * first update. + */ + if ( raw_buffer_size < min_buffer_size) { + + if ( raw_buffer != NULL ) { + + free( raw_buffer ); + + } + + raw_buffer_size = min_buffer_size; + raw_buffer = (char*) malloc( raw_buffer_size ); + + } + + if (!ReadFromRFBServer((char *)&header, sz_rfbZRLEHeader)) + return False; + + remaining = Swap32IfLE(header.length); + + /* Need to initialize the decompressor state. */ + decompStream.next_in = ( Bytef * )buffer; + decompStream.avail_in = 0; + decompStream.next_out = ( Bytef * )raw_buffer; + decompStream.avail_out = raw_buffer_size; + decompStream.data_type = Z_BINARY; + + /* Initialize the decompression stream structures on the first invocation. */ + if ( decompStreamInited == False ) { + + inflateResult = inflateInit( &decompStream ); + + if ( inflateResult != Z_OK ) { + fprintf(stderr, + "inflateInit returned error: %d, msg: %s\n", + inflateResult, + decompStream.msg); + return False; + } + + decompStreamInited = True; + + } + + inflateResult = Z_OK; + + /* Process buffer full of data until no more to process, or + * some type of inflater error, or Z_STREAM_END. + */ + while (( remaining > 0 ) && + ( inflateResult == Z_OK )) { + + if ( remaining > BUFFER_SIZE ) { + toRead = BUFFER_SIZE; + } + else { + toRead = remaining; + } + + /* Fill the buffer, obtaining data from the server. */ + if (!ReadFromRFBServer(buffer,toRead)) + return False; + + decompStream.next_in = ( Bytef * )buffer; + decompStream.avail_in = toRead; + + /* Need to uncompress buffer full. */ + inflateResult = inflate( &decompStream, Z_SYNC_FLUSH ); + + /* We never supply a dictionary for compression. */ + if ( inflateResult == Z_NEED_DICT ) { + fprintf(stderr, "zlib inflate needs a dictionary!\n"); + return False; + } + if ( inflateResult < 0 ) { + fprintf(stderr, + "zlib inflate returned error: %d, msg: %s\n", + inflateResult, + decompStream.msg); + return False; + } + + /* Result buffer allocated to be at least large enough. We should + * never run out of space! + */ + if (( decompStream.avail_in > 0 ) && + ( decompStream.avail_out <= 0 )) { + fprintf(stderr, "zlib inflate ran out of space!\n"); + return False; + } + + remaining -= toRead; + + } /* while ( remaining > 0 ) */ + + if ( inflateResult == Z_OK ) { + void* buf=raw_buffer; + int i,j; + + remaining = raw_buffer_size-decompStream.avail_out; + + for(j=0; j<rh; j+=rfbZRLETileHeight) + for(i=0; i<rw; i+=rfbZRLETileWidth) { + int subWidth=(i+rfbZRLETileWidth>rw)?rw-i:rfbZRLETileWidth; + int subHeight=(j+rfbZRLETileHeight>rh)?rh-j:rfbZRLETileHeight; + int result=HandleZRLETile(buf,remaining,rx+i,ry+j,subWidth,subHeight); + + if(result<0) { + fprintf(stderr, "ZRLE decoding failed (%d)\n",result); +return True; + return False; + } + + buf+=result; + remaining-=result; + } + } + else { + + fprintf(stderr, + "zlib inflate returned error: %d, msg: %s\n", + inflateResult, + decompStream.msg); + return False; + + } + + return True; +} + +#if REALBPP!=BPP && defined(UNCOMP) && UNCOMP!=0 +# if BPP == 32 && IS_BIG_ENDIAN +# define UncompressCPixel(p) ( (*p << myFormat.redShift) | (*(p+1) << myFormat.greenShift) | (*(p+2) << myFormat.blueShift) ) +# else +# if UNCOMP>0 +# define UncompressCPixel(pointer) ((*(CARDBPP*)pointer)>>UNCOMP) +# else +# define UncompressCPixel(pointer) ((*(CARDBPP*)pointer)<<(-(UNCOMP))) +# endif +# endif +#else +# define UncompressCPixel(pointer) (*(CARDBPP*)pointer) +#endif + +extern XImage *image; +extern XImage *image_scale; +extern int skip_maybe_sync; + +static int HandleZRLETile( + unsigned char* buffer,size_t buffer_length, + int x,int y,int w,int h) { + unsigned char* buffer_copy = buffer; + unsigned char* buffer_end = buffer+buffer_length; + unsigned char type; + + if(buffer_length<1) + return -2; + + if (frameBufferLen < w * h * BPP/8) { + if(frameBuffer) { + free(frameBuffer); + } + frameBufferLen = w * h * BPP/8 * 2; + frameBuffer = (unsigned char *) malloc(frameBufferLen); + } + +zywrle_top: + type = *buffer; + buffer++; + switch(type) { + case 0: /* raw */ + { +#if DO_ZYWRLE && BPP != 8 + if (zywrle_level > 0 && !(zywrle_level & 0x80) ) { + zywrle_level |= 0x80; + goto zywrle_top; + } else +#endif + { +#if REALBPP!=BPP + int m0 = 0, i,j; + + + if(1+w*h*REALBPP/8>buffer_length) { + fprintf(stderr, "expected %d bytes, got only %d (%dx%d)\n",1+w*h*REALBPP/8,buffer_length,w,h); + return -3; + } + + for(j=y*si.framebufferWidth; j<(y+h)*si.framebufferWidth; j+=si.framebufferWidth) { + for(i=x; i<x+w; i++,buffer+=REALBPP/8) { +# if 0 + ((CARDBPP*)frameBuffer)[j+i] = UncompressCPixel(buffer); + /* alt */ + CARDBPP color = UncompressCPixel(buffer); + CopyDataToScreen((char *)&color, i, j/si.framebufferWidth, 1, 1); +# else + ((CARDBPP*)frameBuffer)[m0++] = UncompressCPixel(buffer); +# endif + } + } + CopyDataToScreen((char *)frameBuffer, x, y, w, h); +if (0) fprintf(stderr, "cha1: %dx%d+%d+%d\n", w, h, x, y); + +#else +# if 0 + CopyRectangle(buffer, x, y, w, h); +# else + CopyDataToScreen((char *)buffer, x, y, w, h); +# endif + buffer+=w*h*REALBPP/8; +#endif + } + break; + } + case 1: /* solid */ + { + CARDBPP color = UncompressCPixel(buffer); + + if(1+REALBPP/8>buffer_length) + return -4; + + if ((BPP == 8 && appData.useBGR233) || (BPP == 16 && appData.useBGR565)) { + int m0; + for (m0=0; m0 < w*h; m0++) { + ((CARDBPP*)frameBuffer)[m0] = color; + } + CopyDataToScreen((char *)frameBuffer, x, y, w, h); + } else { + FillRectangle(x, y, w, h, color); + } +if (0) fprintf(stderr, "cha2: %dx%d+%d+%d\n", w, h, x, y); + + buffer+=REALBPP/8; + + break; + } + case 2 ... 127: /* packed Palette */ + { + CARDBPP palette[16]; + int m0, i,j,shift, + bpp=(type>4?(type>16?8:4):(type>2?2:1)), + mask=(1<<bpp)-1, + divider=(8/bpp); + + if(1+type*REALBPP/8+((w+divider-1)/divider)*h>buffer_length) + return -5; + + /* read palette */ + for(i=0; i<type; i++,buffer+=REALBPP/8) + palette[i] = UncompressCPixel(buffer); + + m0 = 0; + /* read palettized pixels */ + for(j=y*si.framebufferWidth; j<(y+h)*si.framebufferWidth; j+=si.framebufferWidth) { + for(i=x,shift=8-bpp; i<x+w; i++) { +# if 0 + ((CARDBPP*)frameBuffer)[j+i] = palette[((*buffer)>>shift)&mask]; + /* alt */ + CARDBPP color = palette[((*buffer)>>shift)&mask]; + CopyDataToScreen((char *)&color, i, j/si.framebufferWidth, 1, 1); +# else + ((CARDBPP*)frameBuffer)[m0++] = palette[((*buffer)>>shift)&mask]; +# endif + shift-=bpp; + if(shift<0) { + shift=8-bpp; + buffer++; + } + } + if(shift<8-bpp) + buffer++; + } + CopyDataToScreen((char *)frameBuffer, x, y, w, h); +if (0) fprintf(stderr, "cha3: %dx%d+%d+%d\n", w, h, x, y); + + break; + } + /* case 17 ... 127: not used, but valid */ + case 128: /* plain RLE */ + { + int m0=0, i=0,j=0; + while(j<h) { + int color,length; + /* read color */ + if(buffer+REALBPP/8+1>buffer_end) + return -7; + color = UncompressCPixel(buffer); + buffer+=REALBPP/8; + /* read run length */ + length=1; + while(*buffer==0xff) { + if(buffer+1>=buffer_end) + return -8; + length+=*buffer; + buffer++; + } + length+=*buffer; + buffer++; + while(j<h && length>0) { +# if 0 + ((CARDBPP*)frameBuffer)[(y+j)*si.framebufferWidth+x+i] = color; + /* alt */ + CopyDataToScreen((char *)&color, x+i, y+j, 1, 1); +# else + ((CARDBPP*)frameBuffer)[m0++] = color; +# endif + length--; + i++; + if(i>=w) { + i=0; + j++; + } + } + if(length>0) + fprintf(stderr, "Warning: possible ZRLE corruption\n"); + } + CopyDataToScreen((char *)frameBuffer, x, y, w, h); +if (0) fprintf(stderr, "cha4: %dx%d+%d+%d\n", w, h, x, y); + + break; + } + case 129: /* unused */ + { + return -8; + } + case 130 ... 255: /* palette RLE */ + { + CARDBPP palette[128]; + int m0 = 0, i,j; + + if(2+(type-128)*REALBPP/8>buffer_length) + return -9; + + /* read palette */ + for(i=0; i<type-128; i++,buffer+=REALBPP/8) + palette[i] = UncompressCPixel(buffer); + /* read palettized pixels */ + i=j=0; + while(j<h) { + int color,length; + /* read color */ + if(buffer>=buffer_end) + return -10; + color = palette[(*buffer)&0x7f]; + length=1; + if(*buffer&0x80) { + if(buffer+1>=buffer_end) + return -11; + buffer++; + /* read run length */ + while(*buffer==0xff) { + if(buffer+1>=buffer_end) + return -8; + length+=*buffer; + buffer++; + } + length+=*buffer; + } + buffer++; + while(j<h && length>0) { +# if 0 + ((CARDBPP*)frameBuffer)[(y+j)*si.framebufferWidth+x+i] = color; + /* alt */ + CopyDataToScreen((char *)&color, x+i, y+j, 1, 1); +# else + ((CARDBPP*)frameBuffer)[m0++] = color; +# endif + length--; + i++; + if(i>=w) { + i=0; + j++; + } + } + if(length>0) + fprintf(stderr, "Warning: possible ZRLE corruption\n"); + } + CopyDataToScreen((char *)frameBuffer, x, y, w, h); +if (0) fprintf(stderr, "cha5: %dx%d+%d+%d\n", w, h, x, y); + + break; + } + } + +#if DO_ZYWRLE && BPP != 8 + if (zywrle_level & 0x80) { + int th, tx; + int widthInBytes = w * BPP / 8; + int scrWidthInBytes; + char *scr, *buf; + static CARDBPP *ptmp = NULL; + static int ptmp_len = 0; + XImage *im = image_scale ? image_scale : image; + + if (w * h > ptmp_len) { + ptmp_len = w * h; + if (ptmp_len < rfbZRLETileWidth*rfbZRLETileHeight) { + ptmp_len = rfbZRLETileWidth*rfbZRLETileHeight; + } + if (ptmp) { + free(ptmp); + } + ptmp = (CARDBPP *) malloc(ptmp_len * sizeof(CARDBPP)); + } + + zywrle_level &= 0x7F; + /* Reverse copy: screen to buf/ptmp: */ + /* make this CopyDataFromScreen() or something. */ + if (!appData.useBGR565) { + scrWidthInBytes = si.framebufferWidth * myFormat.bitsPerPixel / 8; + if (scrWidthInBytes != im->bytes_per_line) scrWidthInBytes = im->bytes_per_line; + scr = im->data + y * scrWidthInBytes + x * myFormat.bitsPerPixel / 8; + buf = (char *) ptmp; + + for (th = 0; th < h; th++) { + memcpy(buf, scr, widthInBytes); + buf += widthInBytes; + scr += scrWidthInBytes; + } + } else { + scrWidthInBytes = si.framebufferWidth * 4; + if (scrWidthInBytes != im->bytes_per_line) scrWidthInBytes = im->bytes_per_line; + scr = im->data + y * scrWidthInBytes + x * 4; + buf = (char *) ptmp; + + for (th = 0; th < h; th++) { + for (tx = 0; tx < w; tx++) { + unsigned long pix = *((unsigned int *)scr + tx); + unsigned int r1 = (pix & 0xff0000) >> 16; + unsigned int g1 = (pix & 0x00ff00) >> 8; + unsigned int b1 = (pix & 0x0000ff) >> 0; + int r2, g2, b2, idx; + int rok = 0, gok = 0, bok = 0, is0, sh = 10; + r2 = (31 * r1)/255; + g2 = (63 * g1)/255; + b2 = (31 * b1)/255; + for (is0 = 0; is0 < sh; is0++) { + int is, i, t; + for (i = 0; i < 2; i++) { + if (i == 0) { + is = -is0; + } else { + is = +is0; + } + if (!rok) { + t = r2 + is; + if (r1 == (255 * t)/31) { + r2 = t; rok = 1; + } + } + if (!gok) { + t = g2 + is; + if (g1 == (255 * t)/63) { + g2 = t; gok = 1; + } + } + if (!bok) { + t = b2 + is; + if (b1 == (255 * t)/31) { + b2 = t; bok = 1; + } + } + } + if (rok && gok && bok) { + break; + } + } + idx = (r2 << 11) | (g2 << 5) | (b2 << 0); + *((CARDBPP *)buf + tx) = (CARDBPP) idx; + } + buf += widthInBytes; + scr += scrWidthInBytes; + } + } + ZYWRLE_SYNTHESIZE((PIXEL_T *)ptmp, (PIXEL_T *)ptmp, w, h, w, zywrle_level, zywrleBuf ); + skip_maybe_sync = 1; + + if (appData.yCrop > 0) { + skip_maybe_sync = 0; + } + CopyDataToScreen((char *)ptmp, x, y, w, h); + + } +#endif + + return buffer-buffer_copy; +} + +#undef CARDBPP +#undef CARDREALBPP +#undef HandleZRLE +#undef HandleZRLETile +#undef UncompressCPixel +#undef REALBPP + +#undef UNCOMP + +#undef FillRectangle +#undef IS_BIG_ENDIAN diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/zrleencodetemplate.c vnc_unixsrc/vncviewer/zrleencodetemplate.c --- vnc_unixsrc.orig/vncviewer/zrleencodetemplate.c 1969-12-31 19:00:00.000000000 -0500 +++ vnc_unixsrc/vncviewer/zrleencodetemplate.c 2007-02-04 23:18:09.000000000 -0500 @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * Before including this file, you must define a number of CPP macros. + * + * BPP should be 8, 16 or 32 depending on the bits per pixel. + * GET_IMAGE_INTO_BUF should be some code which gets a rectangle of pixel data + * into the given buffer. EXTRA_ARGS can be defined to pass any other + * arguments needed by GET_IMAGE_INTO_BUF. + * + * Note that the buf argument to ZRLE_ENCODE needs to be at least one pixel + * bigger than the largest tile of pixel data, since the ZRLE encoding + * algorithm writes to the position one past the end of the pixel data. + */ + +#include "zrleoutstream.h" +#include "zrlepalettehelper.h" +#include <assert.h> + +/* __RFB_CONCAT2 concatenates its two arguments. __RFB_CONCAT2E does the same + but also expands its arguments if they are macros */ + +#ifndef __RFB_CONCAT2E +#define __RFB_CONCAT2(a,b) a##b +#define __RFB_CONCAT2E(a,b) __RFB_CONCAT2(a,b) +#endif + +#ifndef __RFB_CONCAT3E +#define __RFB_CONCAT3(a,b,c) a##b##c +#define __RFB_CONCAT3E(a,b,c) __RFB_CONCAT3(a,b,c) +#endif + +#undef END_FIX +#if ZYWRLE_ENDIAN == ENDIAN_LITTLE +# define END_FIX LE +#elif ZYWRLE_ENDIAN == ENDIAN_BIG +# define END_FIX BE +#else +# define END_FIX NE +#endif + +#ifdef CPIXEL +#define PIXEL_T __RFB_CONCAT2E(zrle_U,BPP) +#define zrleOutStreamWRITE_PIXEL __RFB_CONCAT2E(zrleOutStreamWriteOpaque,CPIXEL) +#define ZRLE_ENCODE __RFB_CONCAT3E(zrleEncode,CPIXEL,END_FIX) +#define ZRLE_ENCODE_TILE __RFB_CONCAT3E(zrleEncodeTile,CPIXEL,END_FIX) +#define BPPOUT 24 +#elif BPP==15 +#define PIXEL_T __RFB_CONCAT2E(zrle_U,16) +#define zrleOutStreamWRITE_PIXEL __RFB_CONCAT2E(zrleOutStreamWriteOpaque,16) +#define ZRLE_ENCODE __RFB_CONCAT3E(zrleEncode,BPP,END_FIX) +#define ZRLE_ENCODE_TILE __RFB_CONCAT3E(zrleEncodeTile,BPP,END_FIX) +#define BPPOUT 16 +#else +#define PIXEL_T __RFB_CONCAT2E(zrle_U,BPP) +#define zrleOutStreamWRITE_PIXEL __RFB_CONCAT2E(zrleOutStreamWriteOpaque,BPP) +#define ZRLE_ENCODE __RFB_CONCAT3E(zrleEncode,BPP,END_FIX) +#define ZRLE_ENCODE_TILE __RFB_CONCAT3E(zrleEncodeTile,BPP,END_FIX) +#define BPPOUT BPP +#endif + +#ifndef ZRLE_ONCE +#define ZRLE_ONCE + +static const int bitsPerPackedPixel[] = { + 0, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 +}; + +int zywrle_level; +int zywrleBuf[rfbZRLETileWidth*rfbZRLETileHeight]; + +static zrlePaletteHelper paletteHelper; + +#endif /* ZRLE_ONCE */ + +void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, zrleOutStream* os); + +#if BPP!=8 +#define ZYWRLE_ENCODE +#include "zywrletemplate.c" +#endif + +static void ZRLE_ENCODE (int x, int y, int w, int h, + zrleOutStream* os, void* buf + EXTRA_ARGS + ) +{ + int ty; + for (ty = y; ty < y+h; ty += rfbZRLETileHeight) { + int tx, th = rfbZRLETileHeight; + if (th > y+h-ty) th = y+h-ty; + for (tx = x; tx < x+w; tx += rfbZRLETileWidth) { + int tw = rfbZRLETileWidth; + if (tw > x+w-tx) tw = x+w-tx; + + GET_IMAGE_INTO_BUF(tx,ty,tw,th,buf); + + ZRLE_ENCODE_TILE((PIXEL_T*)buf, tw, th, os); + } + } + zrleOutStreamFlush(os); +} + + +void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, zrleOutStream* os) +{ + /* First find the palette and the number of runs */ + + zrlePaletteHelper *ph; + + int runs = 0; + int singlePixels = 0; + + rfbBool useRle; + rfbBool usePalette; + + int estimatedBytes; + int plainRleBytes; + int i; + + PIXEL_T* ptr = data; + PIXEL_T* end = ptr + h * w; + *end = ~*(end-1); /* one past the end is different so the while loop ends */ + + ph = &paletteHelper; + zrlePaletteHelperInit(ph); + + while (ptr < end) { + PIXEL_T pix = *ptr; + if (*++ptr != pix) { + singlePixels++; + } else { + while (*++ptr == pix) ; + runs++; + } + zrlePaletteHelperInsert(ph, pix); + } + + /* Solid tile is a special case */ + + if (ph->size == 1) { + zrleOutStreamWriteU8(os, 1); + zrleOutStreamWRITE_PIXEL(os, ph->palette[0]); + return; + } + + /* Try to work out whether to use RLE and/or a palette. We do this by + estimating the number of bytes which will be generated and picking the + method which results in the fewest bytes. Of course this may not result + in the fewest bytes after compression... */ + + useRle = FALSE; + usePalette = FALSE; + + estimatedBytes = w * h * (BPPOUT/8); /* start assuming raw */ + +#if BPP!=8 + if( (zywrle_level>0)&& !(zywrle_level & 0x80) ){ + estimatedBytes >>= zywrle_level; + } +#endif + + plainRleBytes = ((BPPOUT/8)+1) * (runs + singlePixels); + + if (plainRleBytes < estimatedBytes) { + useRle = TRUE; + estimatedBytes = plainRleBytes; + } + + if (ph->size < 128) { + int paletteRleBytes = (BPPOUT/8) * ph->size + 2 * runs + singlePixels; + + if (paletteRleBytes < estimatedBytes) { + useRle = TRUE; + usePalette = TRUE; + estimatedBytes = paletteRleBytes; + } + + if (ph->size < 17) { + int packedBytes = ((BPPOUT/8) * ph->size + + w * h * bitsPerPackedPixel[ph->size-1] / 8); + + if (packedBytes < estimatedBytes) { + useRle = FALSE; + usePalette = TRUE; + estimatedBytes = packedBytes; + } + } + } + + if (!usePalette) ph->size = 0; + + zrleOutStreamWriteU8(os, (useRle ? 128 : 0) | ph->size); + + for (i = 0; i < ph->size; i++) { + zrleOutStreamWRITE_PIXEL(os, ph->palette[i]); + } + + if (useRle) { + + PIXEL_T* ptr = data; + PIXEL_T* end = ptr + w * h; + PIXEL_T* runStart; + PIXEL_T pix; + while (ptr < end) { + int len; + runStart = ptr; + pix = *ptr++; + while (*ptr == pix && ptr < end) + ptr++; + len = ptr - runStart; + if (len <= 2 && usePalette) { + int index = zrlePaletteHelperLookup(ph, pix); + if (len == 2) + zrleOutStreamWriteU8(os, index); + zrleOutStreamWriteU8(os, index); + continue; + } + if (usePalette) { + int index = zrlePaletteHelperLookup(ph, pix); + zrleOutStreamWriteU8(os, index | 128); + } else { + zrleOutStreamWRITE_PIXEL(os, pix); + } + len -= 1; + while (len >= 255) { + zrleOutStreamWriteU8(os, 255); + len -= 255; + } + zrleOutStreamWriteU8(os, len); + } + + } else { + + /* no RLE */ + + if (usePalette) { + int bppp; + PIXEL_T* ptr = data; + + /* packed pixels */ + + assert (ph->size < 17); + + bppp = bitsPerPackedPixel[ph->size-1]; + + for (i = 0; i < h; i++) { + zrle_U8 nbits = 0; + zrle_U8 byte = 0; + + PIXEL_T* eol = ptr + w; + + while (ptr < eol) { + PIXEL_T pix = *ptr++; + zrle_U8 index = zrlePaletteHelperLookup(ph, pix); + byte = (byte << bppp) | index; + nbits += bppp; + if (nbits >= 8) { + zrleOutStreamWriteU8(os, byte); + nbits = 0; + } + } + if (nbits > 0) { + byte <<= 8 - nbits; + zrleOutStreamWriteU8(os, byte); + } + } + } else { + + /* raw */ + +#if BPP!=8 + if( (zywrle_level>0)&& !(zywrle_level & 0x80) ){ + ZYWRLE_ANALYZE( data, data, w, h, w, zywrle_level, zywrleBuf ); + zywrle_level |= 0x80; + ZRLE_ENCODE_TILE( data, w, h, os ); + zywrle_level &= 0x7F; + }else +#endif + { +#ifdef CPIXEL + PIXEL_T *ptr; + for (ptr = data; ptr < data+w*h; ptr++) { + zrleOutStreamWRITE_PIXEL(os, *ptr); + } +#else + zrleOutStreamWriteBytes(os, (zrle_U8 *)data, w*h*(BPP/8)); +#endif + } + } + } +} + +#undef PIXEL_T +#undef zrleOutStreamWRITE_PIXEL +#undef ZRLE_ENCODE +#undef ZRLE_ENCODE_TILE +#undef ZYWRLE_ENCODE_TILE +#undef BPPOUT diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/zrleoutstream.c vnc_unixsrc/vncviewer/zrleoutstream.c --- vnc_unixsrc.orig/vncviewer/zrleoutstream.c 1969-12-31 19:00:00.000000000 -0500 +++ vnc_unixsrc/vncviewer/zrleoutstream.c 2005-05-15 10:57:54.000000000 -0400 @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "zrleoutstream.h" +#include <stdlib.h> + +#define ZRLE_IN_BUFFER_SIZE 16384 +#define ZRLE_OUT_BUFFER_SIZE 1024 +#undef ZRLE_DEBUG + +static rfbBool zrleBufferAlloc(zrleBuffer *buffer, int size) +{ + buffer->ptr = buffer->start = malloc(size); + if (buffer->start == NULL) { + buffer->end = NULL; + return FALSE; + } + + buffer->end = buffer->start + size; + + return TRUE; +} + +static void zrleBufferFree(zrleBuffer *buffer) +{ + if (buffer->start) + free(buffer->start); + buffer->start = buffer->ptr = buffer->end = NULL; +} + +static rfbBool zrleBufferGrow(zrleBuffer *buffer, int size) +{ + int offset; + + size += buffer->end - buffer->start; + offset = ZRLE_BUFFER_LENGTH (buffer); + + buffer->start = realloc(buffer->start, size); + if (!buffer->start) { + return FALSE; + } + + buffer->end = buffer->start + size; + buffer->ptr = buffer->start + offset; + + return TRUE; +} + +zrleOutStream *zrleOutStreamNew(void) +{ + zrleOutStream *os; + + os = malloc(sizeof(zrleOutStream)); + if (os == NULL) + return NULL; + + if (!zrleBufferAlloc(&os->in, ZRLE_IN_BUFFER_SIZE)) { + free(os); + return NULL; + } + + if (!zrleBufferAlloc(&os->out, ZRLE_OUT_BUFFER_SIZE)) { + zrleBufferFree(&os->in); + free(os); + return NULL; + } + + os->zs.zalloc = Z_NULL; + os->zs.zfree = Z_NULL; + os->zs.opaque = Z_NULL; + if (deflateInit(&os->zs, Z_DEFAULT_COMPRESSION) != Z_OK) { + zrleBufferFree(&os->in); + free(os); + return NULL; + } + + return os; +} + +void zrleOutStreamFree (zrleOutStream *os) +{ + deflateEnd(&os->zs); + zrleBufferFree(&os->in); + zrleBufferFree(&os->out); + free(os); +} + +rfbBool zrleOutStreamFlush(zrleOutStream *os) +{ + os->zs.next_in = os->in.start; + os->zs.avail_in = ZRLE_BUFFER_LENGTH (&os->in); + +#ifdef ZRLE_DEBUG + rfbLog("zrleOutStreamFlush: avail_in %d\n", os->zs.avail_in); +#endif + + while (os->zs.avail_in != 0) { + do { + int ret; + + if (os->out.ptr >= os->out.end && + !zrleBufferGrow(&os->out, os->out.end - os->out.start)) { + rfbLog("zrleOutStreamFlush: failed to grow output buffer\n"); + return FALSE; + } + + os->zs.next_out = os->out.ptr; + os->zs.avail_out = os->out.end - os->out.ptr; + +#ifdef ZRLE_DEBUG + rfbLog("zrleOutStreamFlush: calling deflate, avail_in %d, avail_out %d\n", + os->zs.avail_in, os->zs.avail_out); +#endif + + if ((ret = deflate(&os->zs, Z_SYNC_FLUSH)) != Z_OK) { + rfbLog("zrleOutStreamFlush: deflate failed with error code %d\n", ret); + return FALSE; + } + +#ifdef ZRLE_DEBUG + rfbLog("zrleOutStreamFlush: after deflate: %d bytes\n", + os->zs.next_out - os->out.ptr); +#endif + + os->out.ptr = os->zs.next_out; + } while (os->zs.avail_out == 0); + } + + os->in.ptr = os->in.start; + + return TRUE; +} + +static int zrleOutStreamOverrun(zrleOutStream *os, + int size) +{ +#ifdef ZRLE_DEBUG + rfbLog("zrleOutStreamOverrun\n"); +#endif + + while (os->in.end - os->in.ptr < size && os->in.ptr > os->in.start) { + os->zs.next_in = os->in.start; + os->zs.avail_in = ZRLE_BUFFER_LENGTH (&os->in); + + do { + int ret; + + if (os->out.ptr >= os->out.end && + !zrleBufferGrow(&os->out, os->out.end - os->out.start)) { + rfbLog("zrleOutStreamOverrun: failed to grow output buffer\n"); + return FALSE; + } + + os->zs.next_out = os->out.ptr; + os->zs.avail_out = os->out.end - os->out.ptr; + +#ifdef ZRLE_DEBUG + rfbLog("zrleOutStreamOverrun: calling deflate, avail_in %d, avail_out %d\n", + os->zs.avail_in, os->zs.avail_out); +#endif + + if ((ret = deflate(&os->zs, 0)) != Z_OK) { + rfbLog("zrleOutStreamOverrun: deflate failed with error code %d\n", ret); + return 0; + } + +#ifdef ZRLE_DEBUG + rfbLog("zrleOutStreamOverrun: after deflate: %d bytes\n", + os->zs.next_out - os->out.ptr); +#endif + + os->out.ptr = os->zs.next_out; + } while (os->zs.avail_out == 0); + + /* output buffer not full */ + + if (os->zs.avail_in == 0) { + os->in.ptr = os->in.start; + } else { + /* but didn't consume all the data? try shifting what's left to the + * start of the buffer. + */ + rfbLog("zrleOutStreamOverrun: out buf not full, but in data not consumed\n"); + memmove(os->in.start, os->zs.next_in, os->in.ptr - os->zs.next_in); + os->in.ptr -= os->zs.next_in - os->in.start; + } + } + + if (size > os->in.end - os->in.ptr) + size = os->in.end - os->in.ptr; + + return size; +} + +static int zrleOutStreamCheck(zrleOutStream *os, int size) +{ + if (os->in.ptr + size > os->in.end) { + return zrleOutStreamOverrun(os, size); + } + return size; +} + +void zrleOutStreamWriteBytes(zrleOutStream *os, + const zrle_U8 *data, + int length) +{ + const zrle_U8* dataEnd = data + length; + while (data < dataEnd) { + int n = zrleOutStreamCheck(os, dataEnd - data); + memcpy(os->in.ptr, data, n); + os->in.ptr += n; + data += n; + } +} + +void zrleOutStreamWriteU8(zrleOutStream *os, zrle_U8 u) +{ + zrleOutStreamCheck(os, 1); + *os->in.ptr++ = u; +} + +void zrleOutStreamWriteOpaque8(zrleOutStream *os, zrle_U8 u) +{ + zrleOutStreamCheck(os, 1); + *os->in.ptr++ = u; +} + +void zrleOutStreamWriteOpaque16 (zrleOutStream *os, zrle_U16 u) +{ + zrleOutStreamCheck(os, 2); + *os->in.ptr++ = ((zrle_U8*)&u)[0]; + *os->in.ptr++ = ((zrle_U8*)&u)[1]; +} + +void zrleOutStreamWriteOpaque32 (zrleOutStream *os, zrle_U32 u) +{ + zrleOutStreamCheck(os, 4); + *os->in.ptr++ = ((zrle_U8*)&u)[0]; + *os->in.ptr++ = ((zrle_U8*)&u)[1]; + *os->in.ptr++ = ((zrle_U8*)&u)[2]; + *os->in.ptr++ = ((zrle_U8*)&u)[3]; +} + +void zrleOutStreamWriteOpaque24A(zrleOutStream *os, zrle_U32 u) +{ + zrleOutStreamCheck(os, 3); + *os->in.ptr++ = ((zrle_U8*)&u)[0]; + *os->in.ptr++ = ((zrle_U8*)&u)[1]; + *os->in.ptr++ = ((zrle_U8*)&u)[2]; +} + +void zrleOutStreamWriteOpaque24B(zrleOutStream *os, zrle_U32 u) +{ + zrleOutStreamCheck(os, 3); + *os->in.ptr++ = ((zrle_U8*)&u)[1]; + *os->in.ptr++ = ((zrle_U8*)&u)[2]; + *os->in.ptr++ = ((zrle_U8*)&u)[3]; +} diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/zrleoutstream.h vnc_unixsrc/vncviewer/zrleoutstream.h --- vnc_unixsrc.orig/vncviewer/zrleoutstream.h 1969-12-31 19:00:00.000000000 -0500 +++ vnc_unixsrc/vncviewer/zrleoutstream.h 2004-05-25 06:05:15.000000000 -0400 @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __ZRLE_OUT_STREAM_H__ +#define __ZRLE_OUT_STREAM_H__ + +#include <zlib.h> +#include "zrletypes.h" +#include "rfb/rfb.h" + +typedef struct { + zrle_U8 *start; + zrle_U8 *ptr; + zrle_U8 *end; +} zrleBuffer; + +typedef struct { + zrleBuffer in; + zrleBuffer out; + + z_stream zs; +} zrleOutStream; + +#define ZRLE_BUFFER_LENGTH(b) ((b)->ptr - (b)->start) + +zrleOutStream *zrleOutStreamNew (void); +void zrleOutStreamFree (zrleOutStream *os); +rfbBool zrleOutStreamFlush (zrleOutStream *os); +void zrleOutStreamWriteBytes (zrleOutStream *os, + const zrle_U8 *data, + int length); +void zrleOutStreamWriteU8 (zrleOutStream *os, + zrle_U8 u); +void zrleOutStreamWriteOpaque8 (zrleOutStream *os, + zrle_U8 u); +void zrleOutStreamWriteOpaque16 (zrleOutStream *os, + zrle_U16 u); +void zrleOutStreamWriteOpaque32 (zrleOutStream *os, + zrle_U32 u); +void zrleOutStreamWriteOpaque24A(zrleOutStream *os, + zrle_U32 u); +void zrleOutStreamWriteOpaque24B(zrleOutStream *os, + zrle_U32 u); + +#endif /* __ZRLE_OUT_STREAM_H__ */ diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/zrlepalettehelper.c vnc_unixsrc/vncviewer/zrlepalettehelper.c --- vnc_unixsrc.orig/vncviewer/zrlepalettehelper.c 1969-12-31 19:00:00.000000000 -0500 +++ vnc_unixsrc/vncviewer/zrlepalettehelper.c 2004-05-25 06:05:15.000000000 -0400 @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "zrlepalettehelper.h" +#include <assert.h> +#include <string.h> + +#define ZRLE_HASH(pix) (((pix) ^ ((pix) >> 17)) & 4095) + +void zrlePaletteHelperInit(zrlePaletteHelper *helper) +{ + memset(helper->palette, 0, sizeof(helper->palette)); + memset(helper->index, 255, sizeof(helper->index)); + memset(helper->key, 0, sizeof(helper->key)); + helper->size = 0; +} + +void zrlePaletteHelperInsert(zrlePaletteHelper *helper, zrle_U32 pix) +{ + if (helper->size < ZRLE_PALETTE_MAX_SIZE) { + int i = ZRLE_HASH(pix); + + while (helper->index[i] != 255 && helper->key[i] != pix) + i++; + if (helper->index[i] != 255) return; + + helper->index[i] = helper->size; + helper->key[i] = pix; + helper->palette[helper->size] = pix; + } + helper->size++; +} + +int zrlePaletteHelperLookup(zrlePaletteHelper *helper, zrle_U32 pix) +{ + int i = ZRLE_HASH(pix); + + assert(helper->size <= ZRLE_PALETTE_MAX_SIZE); + + while (helper->index[i] != 255 && helper->key[i] != pix) + i++; + if (helper->index[i] != 255) return helper->index[i]; + + return -1; +} diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/zrlepalettehelper.h vnc_unixsrc/vncviewer/zrlepalettehelper.h --- vnc_unixsrc.orig/vncviewer/zrlepalettehelper.h 1969-12-31 19:00:00.000000000 -0500 +++ vnc_unixsrc/vncviewer/zrlepalettehelper.h 2004-05-25 06:05:15.000000000 -0400 @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * The PaletteHelper class helps us build up the palette from pixel data by + * storing a reverse index using a simple hash-table + */ + +#ifndef __ZRLE_PALETTE_HELPER_H__ +#define __ZRLE_PALETTE_HELPER_H__ + +#include "zrletypes.h" + +#define ZRLE_PALETTE_MAX_SIZE 127 + +typedef struct { + zrle_U32 palette[ZRLE_PALETTE_MAX_SIZE]; + zrle_U8 index[ZRLE_PALETTE_MAX_SIZE + 4096]; + zrle_U32 key[ZRLE_PALETTE_MAX_SIZE + 4096]; + int size; +} zrlePaletteHelper; + +void zrlePaletteHelperInit (zrlePaletteHelper *helper); +void zrlePaletteHelperInsert(zrlePaletteHelper *helper, + zrle_U32 pix); +int zrlePaletteHelperLookup(zrlePaletteHelper *helper, + zrle_U32 pix); + +#endif /* __ZRLE_PALETTE_HELPER_H__ */ diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/zrletypes.h vnc_unixsrc/vncviewer/zrletypes.h --- vnc_unixsrc.orig/vncviewer/zrletypes.h 1969-12-31 19:00:00.000000000 -0500 +++ vnc_unixsrc/vncviewer/zrletypes.h 2004-05-25 06:05:15.000000000 -0400 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __ZRLE_TYPES_H__ +#define __ZRLE_TYPES_H__ + +typedef unsigned char zrle_U8; +typedef unsigned short zrle_U16; +typedef unsigned int zrle_U32; +typedef signed char zrle_S8; +typedef signed short zrle_S16; +typedef signed int zrle_S32; + +#endif /* __ZRLE_TYPES_H__ */ diff -Naur -X ./exclude vnc_unixsrc.orig/vncviewer/zywrletemplate.c vnc_unixsrc/vncviewer/zywrletemplate.c --- vnc_unixsrc.orig/vncviewer/zywrletemplate.c 1969-12-31 19:00:00.000000000 -0500 +++ vnc_unixsrc/vncviewer/zywrletemplate.c 2008-02-15 23:33:13.000000000 -0500 @@ -0,0 +1,824 @@ + +/******************************************************************** + * * + * THIS FILE IS PART OF THE 'ZYWRLE' VNC CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A FOLLOWING BSD-STYLE SOURCE LICENSE. * + * PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE 'ZYWRLE' VNC CODEC SOURCE CODE IS (C) COPYRIGHT 2006 * + * BY Hitachi Systems & Services, Ltd. * + * (Noriaki Yamazaki, Research & Developement Center) * * + * * + ******************************************************************** +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Hitachi Systems & Services, Ltd. nor +the names of its contributors may be used to endorse or promote +products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************/ + +/* Change Log: + V0.02 : 2008/02/04 : Fix mis encode/decode when width != scanline + (Thanks Johannes Schindelin, author of LibVNC + Server/Client) + V0.01 : 2007/02/06 : Initial release +*/ + +/* #define ZYWRLE_ENCODE */ +/* #define ZYWRLE_DECODE */ +#define ZYWRLE_QUANTIZE + +/* +[References] + PLHarr: + Senecal, J. G., P. Lindstrom, M. A. Duchaineau, and K. I. Joy, "An Improved N-Bit to N-Bit Reversible Haar-Like Transform," Pacific Graphics 2004, October 2004, pp. 371-380. + EZW: + Shapiro, JM: Embedded Image Coding Using Zerotrees of Wavelet Coefficients, IEEE Trans. Signal. Process., Vol.41, pp.3445-3462 (1993). +*/ + + +/* Template Macro stuffs. */ +#undef ZYWRLE_ANALYZE +#undef ZYWRLE_SYNTHESIZE +#define ZYWRLE_ANALYZE __RFB_CONCAT3E(zywrleAnalyze,BPP,END_FIX) +#define ZYWRLE_SYNTHESIZE __RFB_CONCAT3E(zywrleSynthesize,BPP,END_FIX) + +#define ZYWRLE_RGBYUV __RFB_CONCAT3E(zywrleRGBYUV,BPP,END_FIX) +#define ZYWRLE_YUVRGB __RFB_CONCAT3E(zywrleYUVRGB,BPP,END_FIX) +#define ZYWRLE_YMASK __RFB_CONCAT2E(ZYWRLE_YMASK,BPP) +#define ZYWRLE_UVMASK __RFB_CONCAT2E(ZYWRLE_UVMASK,BPP) +#define ZYWRLE_LOAD_PIXEL __RFB_CONCAT2E(ZYWRLE_LOAD_PIXEL,BPP) +#define ZYWRLE_SAVE_PIXEL __RFB_CONCAT2E(ZYWRLE_SAVE_PIXEL,BPP) + +/* Packing/Unpacking pixel stuffs. + Endian conversion stuffs. */ +#undef S_0 +#undef S_1 +#undef L_0 +#undef L_1 +#undef L_2 +#if ZYWRLE_ENDIAN == ENDIAN_BIG +# define S_0 1 +# define S_1 0 +# define L_0 3 +# define L_1 2 +# define L_2 1 +#else +# define S_0 0 +# define S_1 1 +# define L_0 0 +# define L_1 1 +# define L_2 2 +#endif + +/* Load/Save pixel stuffs. */ +#define ZYWRLE_YMASK15 0xFFFFFFF8 +#define ZYWRLE_UVMASK15 0xFFFFFFF8 +#define ZYWRLE_LOAD_PIXEL15(pSrc,R,G,B) { \ + R = (((unsigned char*)pSrc)[S_1]<< 1)& 0xF8; \ + G = ((((unsigned char*)pSrc)[S_1]<< 6)|(((unsigned char*)pSrc)[S_0]>> 2))& 0xF8; \ + B = (((unsigned char*)pSrc)[S_0]<< 3)& 0xF8; \ +} +#define ZYWRLE_SAVE_PIXEL15(pDst,R,G,B) { \ + R &= 0xF8; \ + G &= 0xF8; \ + B &= 0xF8; \ + ((unsigned char*)pDst)[S_1] = (unsigned char)( (R>>1)|(G>>6) ); \ + ((unsigned char*)pDst)[S_0] = (unsigned char)(((B>>3)|(G<<2))& 0xFF); \ +} +#define ZYWRLE_YMASK16 0xFFFFFFFC +#define ZYWRLE_UVMASK16 0xFFFFFFF8 +#define ZYWRLE_LOAD_PIXEL16(pSrc,R,G,B) { \ + R = ((unsigned char*)pSrc)[S_1] & 0xF8; \ + G = ((((unsigned char*)pSrc)[S_1]<< 5)|(((unsigned char*)pSrc)[S_0]>> 3))& 0xFC; \ + B = (((unsigned char*)pSrc)[S_0]<< 3)& 0xF8; \ +} +#define ZYWRLE_SAVE_PIXEL16(pDst,R,G,B) { \ + R &= 0xF8; \ + G &= 0xFC; \ + B &= 0xF8; \ + ((unsigned char*)pDst)[S_1] = (unsigned char)( R |(G>>5) ); \ + ((unsigned char*)pDst)[S_0] = (unsigned char)(((B>>3)|(G<<3))& 0xFF); \ +} +#define ZYWRLE_YMASK32 0xFFFFFFFF +#define ZYWRLE_UVMASK32 0xFFFFFFFF +#define ZYWRLE_LOAD_PIXEL32(pSrc,R,G,B) { \ + R = ((unsigned char*)pSrc)[L_2]; \ + G = ((unsigned char*)pSrc)[L_1]; \ + B = ((unsigned char*)pSrc)[L_0]; \ +} +#define ZYWRLE_SAVE_PIXEL32(pDst,R,G,B) { \ + ((unsigned char*)pDst)[L_2] = (unsigned char)R; \ + ((unsigned char*)pDst)[L_1] = (unsigned char)G; \ + ((unsigned char*)pDst)[L_0] = (unsigned char)B; \ +} + +#ifndef ZYWRLE_ONCE +#define ZYWRLE_ONCE + +#ifdef WIN32 +#define InlineX __inline +#else +#define InlineX inline +#endif + +#ifdef ZYWRLE_ENCODE +/* Tables for Coefficients filtering. */ +# ifndef ZYWRLE_QUANTIZE +/* Type A:lower bit omitting of EZW style. */ +const static unsigned int zywrleParam[3][3]={ + {0x0000F000,0x00000000,0x00000000}, + {0x0000C000,0x00F0F0F0,0x00000000}, + {0x0000C000,0x00C0C0C0,0x00F0F0F0}, +/* {0x0000FF00,0x00000000,0x00000000}, + {0x0000FF00,0x00FFFFFF,0x00000000}, + {0x0000FF00,0x00FFFFFF,0x00FFFFFF}, */ +}; +# else +/* Type B:Non liner quantization filter. */ +static const signed char zywrleConv[4][256]={ +{ /* bi=5, bo=5 r=0.0:PSNR=24.849 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}, +{ /* bi=5, bo=5 r=2.0:PSNR=74.031 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 64, 64, 64, 64, + 64, 64, 64, 64, 72, 72, 72, 72, + 72, 72, 72, 72, 80, 80, 80, 80, + 80, 80, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 96, 96, + 96, 96, 96, 104, 104, 104, 104, 104, + 104, 104, 104, 104, 104, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 0, -120, -120, -120, -120, -120, -120, -120, + -120, -120, -120, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -104, -104, -104, -104, + -104, -104, -104, -104, -104, -104, -96, -96, + -96, -96, -96, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -80, + -80, -80, -80, -80, -80, -72, -72, -72, + -72, -72, -72, -72, -72, -64, -64, -64, + -64, -64, -64, -64, -64, -56, -56, -56, + -56, -56, -56, -56, -56, -56, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, + -48, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}, +{ /* bi=5, bo=4 r=2.0:PSNR=64.441 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 0, -120, -120, -120, -120, -120, -120, -120, + -120, -120, -120, -120, -120, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -104, -104, + -104, -104, -104, -104, -104, -104, -104, -104, + -104, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -80, -80, -80, -80, + -80, -80, -80, -80, -80, -80, -80, -80, + -80, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, + -64, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, + -48, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}, +{ /* bi=5, bo=2 r=2.0:PSNR=43.175 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 0, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +} +}; +const static signed char* zywrleParam[3][3][3]={ + {{zywrleConv[0],zywrleConv[2],zywrleConv[0]},{zywrleConv[0],zywrleConv[0],zywrleConv[0]},{zywrleConv[0],zywrleConv[0],zywrleConv[0]}}, + {{zywrleConv[0],zywrleConv[3],zywrleConv[0]},{zywrleConv[1],zywrleConv[1],zywrleConv[1]},{zywrleConv[0],zywrleConv[0],zywrleConv[0]}}, + {{zywrleConv[0],zywrleConv[3],zywrleConv[0]},{zywrleConv[2],zywrleConv[2],zywrleConv[2]},{zywrleConv[1],zywrleConv[1],zywrleConv[1]}}, +}; +# endif +#endif + +static InlineX void Harr(signed char* pX0, signed char* pX1) +{ + /* Piecewise-Linear Harr(PLHarr) */ + int X0 = (int)*pX0, X1 = (int)*pX1; + int orgX0 = X0, orgX1 = X1; + if ((X0 ^ X1) & 0x80) { + /* differ sign */ + X1 += X0; + if (((X1^orgX1)&0x80)==0) { + /* |X1| > |X0| */ + X0 -= X1; /* H = -B */ + } + } else { + /* same sign */ + X0 -= X1; + if (((X0 ^ orgX0) & 0x80) == 0) { + /* |X0| > |X1| */ + X1 += X0; /* L = A */ + } + } + *pX0 = (signed char)X1; + *pX1 = (signed char)X0; +} +/* + 1D-Wavelet transform. + + In coefficients array, the famous 'pyramid' decomposition is well used. + + 1D Model: + |L0L0L0L0|L0L0L0L0|H0H0H0H0|H0H0H0H0| : level 0 + |L1L1L1L1|H1H1H1H1|H0H0H0H0|H0H0H0H0| : level 1 + + But this method needs line buffer because H/L is different position from X0/X1. + So, I used 'interleave' decomposition instead of it. + + 1D Model: + |L0H0L0H0|L0H0L0H0|L0H0L0H0|L0H0L0H0| : level 0 + |L1H0H1H0|L1H0H1H0|L1H0H1H0|L1H0H1H0| : level 1 + + In this method, H/L and X0/X1 is always same position. + This lead us to more speed and less memory. + Of cause, the result of both method is quite same + because it's only difference that coefficient position. +*/ +static InlineX void WaveletLevel(int* data, int size, int l, int SkipPixel) +{ + int s, ofs; + signed char* pX0; + signed char* end; + + pX0 = (signed char*)data; + s = (8<<l)*SkipPixel; + end = pX0+(size>>(l+1))*s; + s -= 2; + ofs = (4<<l)*SkipPixel; + while (pX0 < end) { + Harr(pX0, pX0+ofs); + pX0++; + Harr(pX0, pX0+ofs); + pX0++; + Harr(pX0, pX0+ofs); + pX0 += s; + } +} +#define InvWaveletLevel(d,s,l,pix) WaveletLevel(d,s,l,pix) + +#ifdef ZYWRLE_ENCODE +# ifndef ZYWRLE_QUANTIZE +/* Type A:lower bit omitting of EZW style. */ +static InlineX void FilterWaveletSquare(int* pBuf, int width, int height, int level, int l) +{ + int r, s; + int x, y; + int* pH; + const unsigned int* pM; + + pM = &(zywrleParam[level-1][l]); + s = 2<<l; + for (r = 1; r < 4; r++) { + pH = pBuf; + if (r & 0x01) + pH += s>>1; + if (r & 0x02) + pH += (s>>1)*width; + for (y = 0; y < height / s; y++) { + for (x = 0; x < width / s; x++) { + /* + these are same following code. + pH[x] = pH[x] / (~pM[x]+1) * (~pM[x]+1); + ( round pH[x] with pM[x] bit ) + '&' operator isn't 'round' but is 'floor'. + So, we must offset when pH[x] is negative. + */ + if (((signed char*)pH)[0] & 0x80) + ((signed char*)pH)[0] += ~((signed char*)pM)[0]; + if (((signed char*)pH)[1] & 0x80) + ((signed char*)pH)[1] += ~((signed char*)pM)[1]; + if (((signed char*)pH)[2] & 0x80) + ((signed char*)pH)[2] += ~((signed char*)pM)[2]; + *pH &= *pM; + pH += s; + } + pH += (s-1)*width; + } + } +} +# else +/* + Type B:Non liner quantization filter. + + Coefficients have Gaussian curve and smaller value which is + large part of coefficients isn't more important than larger value. + So, I use filter of Non liner quantize/dequantize table. + In general, Non liner quantize formula is explained as following. + + y=f(x) = sign(x)*round( ((abs(x)/(2^7))^ r )* 2^(bo-1) )*2^(8-bo) + x=f-1(y) = sign(y)*round( ((abs(y)/(2^7))^(1/r))* 2^(bi-1) )*2^(8-bi) + ( r:power coefficient bi:effective MSB in input bo:effective MSB in output ) + + r < 1.0 : Smaller value is more important than larger value. + r > 1.0 : Larger value is more important than smaller value. + r = 1.0 : Liner quantization which is same with EZW style. + + r = 0.75 is famous non liner quantization used in MP3 audio codec. + In contrast to audio data, larger value is important in wavelet coefficients. + So, I select r = 2.0 table( quantize is x^2, dequantize sqrt(x) ). + + As compared with EZW style liner quantization, this filter tended to be + more sharp edge and be more compression rate but be more blocking noise and be less quality. + Especially, the surface of graphic objects has distinguishable noise in middle quality mode. + + We need only quantized-dequantized(filtered) value rather than quantized value itself + because all values are packed or palette-lized in later ZRLE section. + This lead us not to need to modify client decoder when we change + the filtering procedure in future. + Client only decodes coefficients given by encoder. +*/ +static InlineX void FilterWaveletSquare(int* pBuf, int width, int height, int level, int l) +{ + int r, s; + int x, y; + int* pH; + const signed char** pM; + + pM = zywrleParam[level-1][l]; + s = 2<<l; + for (r = 1; r < 4; r++) { + pH = pBuf; + if (r & 0x01) + pH += s>>1; + if (r & 0x02) + pH += (s>>1)*width; + for (y = 0; y < height / s; y++) { + for (x = 0; x < width / s; x++) { + ((signed char*)pH)[0] = pM[0][((unsigned char*)pH)[0]]; + ((signed char*)pH)[1] = pM[1][((unsigned char*)pH)[1]]; + ((signed char*)pH)[2] = pM[2][((unsigned char*)pH)[2]]; + pH += s; + } + pH += (s-1)*width; + } + } +} +# endif + +static InlineX void Wavelet(int* pBuf, int width, int height, int level) +{ + int l, s; + int* pTop; + int* pEnd; + + for (l = 0; l < level; l++) { + pTop = pBuf; + pEnd = pBuf+height*width; + s = width<<l; + while (pTop < pEnd) { + WaveletLevel(pTop, width, l, 1); + pTop += s; + } + pTop = pBuf; + pEnd = pBuf+width; + s = 1<<l; + while (pTop < pEnd) { + WaveletLevel(pTop, height,l, width); + pTop += s; + } + FilterWaveletSquare(pBuf, width, height, level, l); + } +} +#endif +#ifdef ZYWRLE_DECODE +static InlineX void InvWavelet(int* pBuf, int width, int height, int level) +{ + int l, s; + int* pTop; + int* pEnd; + + for (l = level - 1; l >= 0; l--) { + pTop = pBuf; + pEnd = pBuf+width; + s = 1<<l; + while (pTop < pEnd) { + InvWaveletLevel(pTop, height,l, width); + pTop += s; + } + pTop = pBuf; + pEnd = pBuf+height*width; + s = width<<l; + while (pTop < pEnd) { + InvWaveletLevel(pTop, width, l, 1); + pTop += s; + } + } +} +#endif + +/* Load/Save coefficients stuffs. + Coefficients manages as 24 bits little-endian pixel. */ +#define ZYWRLE_LOAD_COEFF(pSrc,R,G,B) { \ + R = ((signed char*)pSrc)[2]; \ + G = ((signed char*)pSrc)[1]; \ + B = ((signed char*)pSrc)[0]; \ +} +#define ZYWRLE_SAVE_COEFF(pDst,R,G,B) { \ + ((signed char*)pDst)[2] = (signed char)R; \ + ((signed char*)pDst)[1] = (signed char)G; \ + ((signed char*)pDst)[0] = (signed char)B; \ +} + +/* + RGB <=> YUV conversion stuffs. + YUV coversion is explained as following formula in strict meaning: + Y = 0.299R + 0.587G + 0.114B ( 0<=Y<=255) + U = -0.169R - 0.331G + 0.500B (-128<=U<=127) + V = 0.500R - 0.419G - 0.081B (-128<=V<=127) + + I use simple conversion RCT(reversible color transform) which is described + in JPEG-2000 specification. + Y = (R + 2G + B)/4 ( 0<=Y<=255) + U = B-G (-256<=U<=255) + V = R-G (-256<=V<=255) +*/ +#define ROUND(x) (((x)<0)?0:(((x)>255)?255:(x))) + /* RCT is N-bit RGB to N-bit Y and N+1-bit UV. + For make Same N-bit, UV is lossy. + More exact PLHarr, we reduce to odd range(-127<=x<=127). */ +#define ZYWRLE_RGBYUV1(R,G,B,Y,U,V,ymask,uvmask) { \ + Y = (R+(G<<1)+B)>>2; \ + U = B-G; \ + V = R-G; \ + Y -= 128; \ + U >>= 1; \ + V >>= 1; \ + Y &= ymask; \ + U &= uvmask; \ + V &= uvmask; \ + if (Y == -128) \ + Y += (0xFFFFFFFF-ymask+1); \ + if (U == -128) \ + U += (0xFFFFFFFF-uvmask+1); \ + if (V == -128) \ + V += (0xFFFFFFFF-uvmask+1); \ +} +#define ZYWRLE_YUVRGB1(R,G,B,Y,U,V) { \ + Y += 128; \ + U <<= 1; \ + V <<= 1; \ + G = Y-((U+V)>>2); \ + B = U+G; \ + R = V+G; \ + G = ROUND(G); \ + B = ROUND(B); \ + R = ROUND(R); \ +} + +/* + coefficient packing/unpacking stuffs. + Wavelet transform makes 4 sub coefficient image from 1 original image. + + model with pyramid decomposition: + +------+------+ + | | | + | L | Hx | + | | | + +------+------+ + | | | + | H | Hxy | + | | | + +------+------+ + + So, we must transfer each sub images individually in strict meaning. + But at least ZRLE meaning, following one decompositon image is same as + avobe individual sub image. I use this format. + (Strictly saying, transfer order is reverse(Hxy->Hy->Hx->L) + for simplified procedure for any wavelet level.) + + +------+------+ + | L | + +------+------+ + | Hx | + +------+------+ + | Hy | + +------+------+ + | Hxy | + +------+------+ +*/ +#define INC_PTR(data) \ + data++; \ + if( data-pData >= (w+uw) ){ \ + data += scanline-(w+uw); \ + pData = data; \ + } + +#define ZYWRLE_TRANSFER_COEFF(pBuf,data,r,w,h,scanline,level,TRANS) \ + pH = pBuf; \ + s = 2<<level; \ + if (r & 0x01) \ + pH += s>>1; \ + if (r & 0x02) \ + pH += (s>>1)*w; \ + pEnd = pH+h*w; \ + while (pH < pEnd) { \ + pLine = pH+w; \ + while (pH < pLine) { \ + TRANS \ + INC_PTR(data) \ + pH += s; \ + } \ + pH += (s-1)*w; \ + } + +#define ZYWRLE_PACK_COEFF(pBuf,data,r,width,height,scanline,level) \ + ZYWRLE_TRANSFER_COEFF(pBuf,data,r,width,height,scanline,level,ZYWRLE_LOAD_COEFF(pH,R,G,B);ZYWRLE_SAVE_PIXEL(data,R,G,B);) + +#define ZYWRLE_UNPACK_COEFF(pBuf,data,r,width,height,scanline,level) \ + ZYWRLE_TRANSFER_COEFF(pBuf,data,r,width,height,scanline,level,ZYWRLE_LOAD_PIXEL(data,R,G,B);ZYWRLE_SAVE_COEFF(pH,R,G,B);) + +#define ZYWRLE_SAVE_UNALIGN(data,TRANS) \ + pTop = pBuf+w*h; \ + pEnd = pBuf + (w+uw)*(h+uh); \ + while (pTop < pEnd) { \ + TRANS \ + INC_PTR(data) \ + pTop++; \ + } + +#define ZYWRLE_LOAD_UNALIGN(data,TRANS) \ + pTop = pBuf+w*h; \ + if (uw) { \ + pData= data + w; \ + pEnd = (int*)(pData+ h*scanline); \ + while (pData < (PIXEL_T*)pEnd) { \ + pLine = (int*)(pData + uw); \ + while (pData < (PIXEL_T*)pLine) { \ + TRANS \ + pData++; \ + pTop++; \ + } \ + pData += scanline-uw; \ + } \ + } \ + if (uh) { \ + pData= data + h*scanline; \ + pEnd = (int*)(pData+ uh*scanline); \ + while (pData < (PIXEL_T*)pEnd) { \ + pLine = (int*)(pData + w); \ + while (pData < (PIXEL_T*)pLine) { \ + TRANS \ + pData++; \ + pTop++; \ + } \ + pData += scanline-w; \ + } \ + } \ + if (uw && uh) { \ + pData= data + w+ h*scanline; \ + pEnd = (int*)(pData+ uh*scanline); \ + while (pData < (PIXEL_T*)pEnd) { \ + pLine = (int*)(pData + uw); \ + while (pData < (PIXEL_T*)pLine) { \ + TRANS \ + pData++; \ + pTop++; \ + } \ + pData += scanline-uw; \ + } \ + } + +static InlineX void zywrleCalcSize(int* pW, int* pH, int level) +{ + *pW &= ~((1<<level)-1); + *pH &= ~((1<<level)-1); +} + +#endif /* ZYWRLE_ONCE */ + +#ifndef CPIXEL +#ifdef ZYWRLE_ENCODE +static InlineX void ZYWRLE_RGBYUV(int* pBuf, PIXEL_T* data, int width, int height, int scanline) +{ + int R, G, B; + int Y, U, V; + int* pLine; + int* pEnd; + pEnd = pBuf+height*width; + while (pBuf < pEnd) { + pLine = pBuf+width; + while (pBuf < pLine) { + ZYWRLE_LOAD_PIXEL(data,R,G,B); + ZYWRLE_RGBYUV1(R,G,B,Y,U,V,ZYWRLE_YMASK,ZYWRLE_UVMASK); + ZYWRLE_SAVE_COEFF(pBuf,V,Y,U); + pBuf++; + data++; + } + data += scanline-width; + } +} +#endif +#ifdef ZYWRLE_DECODE +static InlineX void ZYWRLE_YUVRGB(int* pBuf, PIXEL_T* data, int width, int height, int scanline) { + int R, G, B; + int Y, U, V; + int* pLine; + int* pEnd; + pEnd = pBuf+height*width; + while (pBuf < pEnd) { + pLine = pBuf+width; + while (pBuf < pLine) { + ZYWRLE_LOAD_COEFF(pBuf,V,Y,U); + ZYWRLE_YUVRGB1(R,G,B,Y,U,V); + ZYWRLE_SAVE_PIXEL(data,R,G,B); + pBuf++; + data++; + } + data += scanline-width; + } +} +#endif + +#ifdef ZYWRLE_ENCODE +PIXEL_T* ZYWRLE_ANALYZE(PIXEL_T* dst, PIXEL_T* src, int w, int h, int scanline, int level, int* pBuf) { + int l; + int uw = w; + int uh = h; + int* pTop; + int* pEnd; + int* pLine; + PIXEL_T* pData; + int R, G, B; + int s; + int* pH; + + zywrleCalcSize(&w, &h, level); + if (w == 0 || h == 0) + return NULL; + uw -= w; + uh -= h; + + pData = dst; + ZYWRLE_LOAD_UNALIGN(src,*(PIXEL_T*)pTop=*pData;) + ZYWRLE_RGBYUV(pBuf, src, w, h, scanline); + Wavelet(pBuf, w, h, level); + for (l = 0; l < level; l++) { + ZYWRLE_PACK_COEFF(pBuf, dst, 3, w, h, scanline, l); + ZYWRLE_PACK_COEFF(pBuf, dst, 2, w, h, scanline, l); + ZYWRLE_PACK_COEFF(pBuf, dst, 1, w, h, scanline, l); + if (l == level - 1) { + ZYWRLE_PACK_COEFF(pBuf, dst, 0, w, h, scanline, l); + } + } + ZYWRLE_SAVE_UNALIGN(dst,*dst=*(PIXEL_T*)pTop;) + return dst; +} +#endif +#ifdef ZYWRLE_DECODE +PIXEL_T* ZYWRLE_SYNTHESIZE(PIXEL_T* dst, PIXEL_T* src, int w, int h, int scanline, int level, int* pBuf) +{ + int l; + int uw = w; + int uh = h; + int* pTop; + int* pEnd; + int* pLine; + PIXEL_T* pData; + int R, G, B; + int s; + int* pH; + + zywrleCalcSize(&w, &h, level); + if (w == 0 || h == 0) + return NULL; + uw -= w; + uh -= h; + + pData = src; + for (l = 0; l < level; l++) { + ZYWRLE_UNPACK_COEFF(pBuf, src, 3, w, h, scanline, l); + ZYWRLE_UNPACK_COEFF(pBuf, src, 2, w, h, scanline, l); + ZYWRLE_UNPACK_COEFF(pBuf, src, 1, w, h, scanline, l); + if (l == level - 1) { + ZYWRLE_UNPACK_COEFF(pBuf, src, 0, w, h, scanline, l); + } + } + ZYWRLE_SAVE_UNALIGN(src,*(PIXEL_T*)pTop=*src;) + InvWavelet(pBuf, w, h, level); + ZYWRLE_YUVRGB(pBuf, dst, w, h, scanline); + ZYWRLE_LOAD_UNALIGN(dst,*pData=*(PIXEL_T*)pTop;) + return src; +} +#endif +#endif /* CPIXEL */ + +#undef ZYWRLE_RGBYUV +#undef ZYWRLE_YUVRGB +#undef ZYWRLE_LOAD_PIXEL +#undef ZYWRLE_SAVE_PIXEL diff -Naur -X ./exclude vnc_unixsrc.orig/include/rfbproto.h vnc_unixsrc/include/rfbproto.h --- vnc_unixsrc.orig/include/rfbproto.h 2004-05-27 03:02:02.000000000 -0400 +++ vnc_unixsrc/include/rfbproto.h 2010-02-25 21:54:58.000000000 -0500 @@ -205,7 +205,22 @@ #define rfbSecTypeInvalid 0 #define rfbSecTypeNone 1 #define rfbSecTypeVncAuth 2 +#define rfbSecTypeRA2 5 +#define rfbSecTypeRA2ne 6 #define rfbSecTypeTight 16 +#define rfbSecTypeUltra 17 + +/* try to support VeNCrypt and TLS */ +#define rfbSecTypeAnonTls 18 +#define rfbSecTypeVencrypt 19 + +#define rfbVencryptPlain 256 +#define rfbVencryptTlsNone 257 +#define rfbVencryptTlsVnc 258 +#define rfbVencryptTlsPlain 259 +#define rfbVencryptX509None 260 +#define rfbVencryptX509Vnc 261 +#define rfbVencryptX509Plain 262 /*----------------------------------------------------------------------------- @@ -381,6 +396,11 @@ #define rfbBell 2 #define rfbServerCutText 3 +#define rfbResizeFrameBuffer 4 /* Modif sf@2002 */ + +/* http://sourceforge.net/projects/vncsessmgr */ +#define rfbRestartConnection 82 + #define rfbFileListData 130 #define rfbFileDownloadData 131 #define rfbFileUploadCancel 132 @@ -403,6 +423,18 @@ #define rfbPointerEvent 5 #define rfbClientCutText 6 +/* ultra */ + +#define rfbFileTransfer 7 +#define rfbSetScale 8 +#define rfbSetServerInput 9 +#define rfbSetSW 10 +#define rfbTextChat 11 +#define rfbKeyFrameRequest 12 +#define rfbPalmVNCSetScaleFactor 0xF + + + #define rfbFileListRequest 130 #define rfbFileDownloadRequest 131 #define rfbFileUploadRequest 132 @@ -435,6 +467,13 @@ #define rfbEncodingTight 7 #define rfbEncodingZlibHex 8 +#define rfbEncodingZRLE 16 +/* +nyama/2006/08/02:new YUV-Wavlet lossy codec based on ZRLE (ZYWRLE) + */ +#define rfbEncodingZYWRLE 17 + + /* signatures for basic encoding types */ #define sig_rfbEncodingRaw "RAW_____" #define sig_rfbEncodingCopyRect "COPYRECT" @@ -955,6 +994,51 @@ #define sz_rfbFileDownloadFailedMsg 4 /*----------------------------------------------------------------------------- + * RestartConnection - the server has restarted the client connection. + */ + +typedef struct _rfbRestartConnectionMsg { + CARD8 type; /* always rfbRestartConnection */ + CARD8 pad1; + CARD16 pad2; + CARD32 length; + /* followed by char text[length] */ +} rfbRestartConnectionMsg; + +#define sz_rfbRestartConnectionMsg 8 + + +typedef struct _rfbTextChatMsg { + CARD8 type; /* always rfbTextChat */ + CARD8 pad1; /* Could be used later as an additionnal param */ + CARD16 pad2; /* Could be used later as text offset, for instance */ + CARD32 length; /* Specific values for Open, close, finished (-1, -2, -3) */ + /* followed by char text[length] */ +} rfbTextChatMsg; + +#define sz_rfbTextChatMsg 8 + +#define rfbTextMaxSize 4096 +#define rfbTextChatOpen 0xFFFFFFFF +#define rfbTextChatClose 0xFFFFFFFE +#define rfbTextChatFinished 0xFFFFFFFD + +/*----------------------------------------------------------------------------- + * Modif sf@2002 + * ResizeFrameBuffer - The Client must change the size of its framebuffer + */ + +typedef struct _rfbResizeFrameBufferMsg { + CARD8 type; /* always rfbResizeFrameBuffer */ + CARD8 pad1; + CARD16 framebufferWidth; /* FrameBuffer width */ + CARD16 framebufferHeight; /* FrameBuffer height */ +} rfbResizeFrameBufferMsg; + +#define sz_rfbResizeFrameBufferMsg 6 + + +/*----------------------------------------------------------------------------- * Union of all server->client messages. */ @@ -968,6 +1052,8 @@ rfbFileDownloadDataMsg fdd; rfbFileUploadCancelMsg fuc; rfbFileDownloadFailedMsg fdf; + rfbRestartConnectionMsg rc; + rfbTextChatMsg tc; } rfbServerToClientMsg; @@ -1221,6 +1307,41 @@ #define sz_rfbFileCreateDirRequestMsg 4 +/* ultra */ +typedef struct _rfbSetScaleMsg { + CARD8 type; /* always rfbSetScale */ + CARD8 scale; /* Scale value 1<sv<n */ + CARD16 pad; +} rfbSetScaleMsg; + +#define sz_rfbSetScaleMsg 4 + +typedef struct { + CARD8 type; /* always rfbSetScaleFactor */ + + CARD8 scale; /* Scale factor (positive non-zero integer) */ + CARD16 pad2; +} rfbPalmVNCSetScaleFactorMsg; + +#define sz_rfbPalmVNCSetScaleFactorMsg (4) + +typedef struct _rfbSetServerInputMsg { + CARD8 type; /* always rfbSetServerInputMsg */ + CARD8 status; /* on or off */ + CARD16 pad; +} rfbSetServerInputMsg; + +#define sz_rfbSetServerInputMsg 4 + +typedef struct _rfbSetSWMsg { + CARD8 type; /* always rfbSetSW */ + CARD8 status; + CARD16 x; + CARD16 y; +} rfbSetSWMsg; + +#define sz_rfbSetSWMsg 6 + /*----------------------------------------------------------------------------- * Union of all client->server messages. */ @@ -1241,4 +1362,9 @@ rfbFileDownloadCancelMsg fdc; rfbFileUploadFailedMsg fuf; rfbFileCreateDirRequestMsg fcdr; + rfbSetScaleMsg ssc; + rfbPalmVNCSetScaleFactorMsg pssf; + rfbSetServerInputMsg sim; + rfbSetSWMsg sw; + rfbTextChatMsg tc; } rfbClientToServerMsg; diff -Naur -X ./exclude vnc_unixsrc.orig/include/vncauth.h vnc_unixsrc/include/vncauth.h --- vnc_unixsrc.orig/include/vncauth.h 2000-06-11 08:00:53.000000000 -0400 +++ vnc_unixsrc/include/vncauth.h 2009-03-21 00:37:23.000000000 -0400 @@ -23,8 +23,11 @@ #define MAXPWLEN 8 #define CHALLENGESIZE 16 +#define CHALLENGESIZE_MSLOGON 64 extern int vncEncryptAndStorePasswd(char *passwd, char *fname); extern char *vncDecryptPasswdFromFile(char *fname); extern void vncRandomBytes(unsigned char *bytes); extern void vncEncryptBytes(unsigned char *bytes, char *passwd); + +extern void vncEncryptPasswd_MSLOGON(unsigned char *encryptedPasswd, char *passwd); diff -Naur -X ./exclude vnc_unixsrc.orig/libvncauth/d3des.c vnc_unixsrc/libvncauth/d3des.c --- vnc_unixsrc.orig/libvncauth/d3des.c 2000-06-11 08:00:53.000000000 -0400 +++ vnc_unixsrc/libvncauth/d3des.c 2010-02-25 21:49:02.000000000 -0500 @@ -34,12 +34,15 @@ static void cookey(unsigned long *); static unsigned long KnL[32] = { 0L }; +/* no londer used: */ +#if 0 static unsigned long KnR[32] = { 0L }; static unsigned long Kn3[32] = { 0L }; static unsigned char Df_Key[24] = { 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10, 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67 }; +#endif static unsigned short bytebit[8] = { 01, 02, 04, 010, 020, 040, 0100, 0200 }; diff -Naur -X ./exclude vnc_unixsrc.orig/libvncauth/vncauth.c vnc_unixsrc/libvncauth/vncauth.c --- vnc_unixsrc.orig/libvncauth/vncauth.c 2003-03-01 11:48:06.000000000 -0500 +++ vnc_unixsrc/libvncauth/vncauth.c 2010-02-25 21:47:25.000000000 -0500 @@ -27,9 +27,11 @@ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> +#include <time.h> #include <vncauth.h> #include <d3des.h> +#include <fcntl.h> /* * Make sure we call srandom() only once. @@ -45,6 +47,8 @@ static unsigned char s_fixedkey[8] = {23,82,107,6,35,78,88,7}; +int vncEncryptAndStorePasswd2(char *passwd, char *passwdViewOnly, char *fname); +int vncDecryptPasswdFromFile2(char *fname, char *passwdFullControl, char *passwdViewOnly); /* * Encrypt a password and store it in a file. Returns 0 if successful, @@ -73,7 +77,7 @@ vncEncryptAndStorePasswd2(char *passwd, char *passwdViewOnly, char *fname) { FILE *fp; - int i, bytesToWrite, bytesWrote; + int bytesToWrite, bytesWrote; unsigned char encryptedPasswd[16] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 @@ -195,6 +199,44 @@ return (i < 16) ? 1 : 2; } +unsigned int urandom(void) { + unsigned int val = 0; + struct stat sb; + int fd = -1; + if (fd < 0 && stat("/dev/urandom", &sb) == 0) { + fd = open("/dev/urandom", O_RDONLY); + } + if (fd < 0 && stat("/dev/random", &sb) == 0) { + fd = open("/dev/random", O_RDONLY); + } + if (fd < 0 && stat("/proc/loadavg", &sb) == 0) { + fd = open("/proc/loadavg", O_RDONLY); + } + if (fd < 0 && stat("/bin/bash", &sb) == 0) { + fd = open("/bin/bash", O_RDONLY); + lseek(fd, (off_t) (unsigned int) getpid(), SEEK_SET); + } + if (fd >= 0) { + int i; + for (i=0; i < 3; i++) { + char buf[2]; + if (read(fd, buf, 1) > 0) { + unsigned char uc = (unsigned char) buf[0]; + if (i==0) { + val += uc; + } else if (i==1) { + val += uc * 256; + } else if (i==2) { + val += uc * 256 * 256; + } + } + } + close(fd); + } else { + val = (unsigned int) getpid(); + } + return val; +} /* * Generate CHALLENGESIZE random bytes for use in challenge-response @@ -207,11 +249,13 @@ int i; unsigned int seed; - if (!s_srandom_called) { - seed = (unsigned int)time(0) ^ (unsigned int)getpid(); - srandom(seed); - s_srandom_called = 1; - } + if (!s_srandom_called) { + seed = (unsigned int)time(0) ^ (unsigned int)getpid(); + seed += urandom(); + + srandom(seed); + s_srandom_called = 1; + } for (i = 0; i < CHALLENGESIZE; i++) { bytes[i] = (unsigned char)(random() & 255); @@ -245,3 +289,48 @@ des(bytes+i, bytes+i); } } + +void UvncEncryptPasswd_MSLOGON(unsigned char *encryptedPasswd, char *passwd) { + unsigned int i; + for (i=0; i < 32; i++) { + if (i < strlen(passwd)) { + encryptedPasswd[i] = passwd[i]; + } else { + encryptedPasswd[i] = '\0'; + } + } + deskey(s_fixedkey, EN0); + des(encryptedPasswd, encryptedPasswd); +} + +void UvncEncryptBytes2(unsigned char *where, int length, unsigned char *key) { + int i, j; + deskey(key, EN0); + for (i=0; i < 8; i++) { + where[i] ^= key[i]; + } + des(where, where); + for (i=8; i < length; i += 8) { + for (j=0; j < 8; j++) { + where[i+j] ^= where[i+j-8]; + } + des(where+i, where+i); + } +} + +void UvncDecryptBytes2(unsigned char *where, int length, unsigned char *key) { + int i, j; + deskey(key, DE1); + for (i = length - 8; i > 0; i -= 8) { + des(where + i, where + i); + for (j=0; j < 8; j++) { + where[i+j] ^= where[i+j-8]; + } + } + /* i=0 */ + des(where, where); + for (i=0; i < 8; i++) { + where[i] ^= key[i]; + } +} +