diff --git a/projector-agent-common/src/main/kotlin/org/jetbrains/projector/agent/common/Misc.kt b/projector-agent-common/src/main/kotlin/org/jetbrains/projector/agent/common/Misc.kt index 6495994bc..8d5a32b3a 100644 --- a/projector-agent-common/src/main/kotlin/org/jetbrains/projector/agent/common/Misc.kt +++ b/projector-agent-common/src/main/kotlin/org/jetbrains/projector/agent/common/Misc.kt @@ -75,6 +75,8 @@ public fun Constructor<*>.toGetDeclaredMethodFormat(): String { public fun loadClassWithProjectorLoader(clazz: Class<*>): String = loadClassWithProjectorLoader(clazz.name, true) +public fun loadClassWithProjectorLoader(className: String): String = loadClassWithProjectorLoader(className, true) + private fun loadClassWithProjectorLoader(className: String, trim: Boolean): String = """ $commonClassLoadCode .loadClass("$className") diff --git a/projector-agent-ij-injector/src/main/kotlin/org/jetbrains/projector/agent/ijInjector/IjJcefTransformer.kt b/projector-agent-ij-injector/src/main/kotlin/org/jetbrains/projector/agent/ijInjector/IjJcefTransformer.kt index da1a4aee5..621bcdea2 100644 --- a/projector-agent-ij-injector/src/main/kotlin/org/jetbrains/projector/agent/ijInjector/IjJcefTransformer.kt +++ b/projector-agent-ij-injector/src/main/kotlin/org/jetbrains/projector/agent/ijInjector/IjJcefTransformer.kt @@ -23,6 +23,8 @@ */ package org.jetbrains.projector.agent.ijInjector +import com.intellij.openapi.actionSystem.ActionGroup +import com.intellij.openapi.fileEditor.LayoutActionsFloatingToolbar import com.intellij.ui.jcef.* import javassist.* import javassist.expr.* @@ -38,6 +40,7 @@ import org.jetbrains.projector.common.intellij.buildAtLeast import org.jetbrains.projector.ij.jcef.CefHandlers import org.jetbrains.projector.ij.jcef.ProjectorCefBrowser import org.jetbrains.projector.util.loading.state.IdeState +import javax.swing.JComponent internal object IjJcefTransformer : IdeTransformerSetup() { @@ -64,6 +67,13 @@ internal object IjJcefTransformer : IdeTransformerSetup String) = """ + { + Class pWindowClass = ${loadClassWithProjectorLoader("org.jetbrains.projector.awt.PWindow")}; + Object pWindow = pWindowClass + .getDeclaredMethod("getWindow", new Class[]{java.awt.Component.class}) + .invoke(null, new Object[]{$component}); + if (pWindow != null) { + java.awt.Graphics2D windowGraphics = (java.awt.Graphics2D) pWindowClass + .getDeclaredMethod("getGraphics", new Class[]{}) + .invoke(pWindow, new Object[]{}); + ${useGraphics("windowGraphics")} + } + } + """.trimIndent() + + private fun transformLayoutActionsFloatingToolbar(ctClass: CtClass): ByteArray { + + ctClass + .getDeclaredConstructor(JComponent::class.java, ActionGroup::class.java) + .insertAfter(createPWindow("this")) + + ctClass + .getDeclaredMethod("dispose") + .insertBefore(disposePWindow("this")) + + listOf( + "paintComponent", + "paintChildren", + ).forEach { methodName -> + ctClass + .getDeclaredMethod(methodName) + .apply { + if (isAgent) { + insertAfter(useGraphics("this") { g -> + """ + if ($g != $1) { + $methodName($g); + } + """.trimIndent() + }) + } else { + insertBefore(useGraphics("this") { g -> "$1 = $g;" }) + } + } + } + + return ctClass.toBytecode() + } + + private fun transformHwFacadeHelper(clazz: CtClass): ByteArray { + + clazz + .getDeclaredMethod("paint") + .setBodyOrInsertAfter(useGraphics("myTarget") { g -> + """ + $g.clearRect(0, 0, myTarget.getWidth(), myTarget.getHeight()); + $2.accept($g); + """.trimIndent() + }) + + clazz + .getDeclaredMethod("addNotify") + .setBody(createPWindow("myTarget")) + + clazz + .getDeclaredMethod("removeNotify") + .setBody(disposePWindow("myTarget")) + + return clazz.toBytecode() + } + private fun transformJBSchemeHandlerFactory(clazz: CtClass): ByteArray { if (isAgent) { diff --git a/projector-client-common/src/commonMain/kotlin/org/jetbrains/projector/client/common/Renderer.kt b/projector-client-common/src/commonMain/kotlin/org/jetbrains/projector/client/common/Renderer.kt index 68f2c7c5a..09069299c 100644 --- a/projector-client-common/src/commonMain/kotlin/org/jetbrains/projector/client/common/Renderer.kt +++ b/projector-client-common/src/commonMain/kotlin/org/jetbrains/projector/client/common/Renderer.kt @@ -388,6 +388,14 @@ class Renderer(private val renderingSurface: RenderingSurface) { } } + fun clearRect(x: Double, y: Double, width: Double, height: Double) { + ensureClip() + ensureTransform() + ensureComposite() + + ctx.clearRect(x, y, width, height) + } + fun paintPolygon(paintType: PaintType, points: List) { ensureClip() ensureTransform() diff --git a/projector-client-common/src/commonMain/kotlin/org/jetbrains/projector/client/common/SingleRenderingSurfaceProcessor.kt b/projector-client-common/src/commonMain/kotlin/org/jetbrains/projector/client/common/SingleRenderingSurfaceProcessor.kt index 26198af18..d0edf1842 100644 --- a/projector-client-common/src/commonMain/kotlin/org/jetbrains/projector/client/common/SingleRenderingSurfaceProcessor.kt +++ b/projector-client-common/src/commonMain/kotlin/org/jetbrains/projector/client/common/SingleRenderingSurfaceProcessor.kt @@ -129,6 +129,13 @@ class SingleRenderingSurfaceProcessor(private val renderingSurface: RenderingSur r2 = arcHeight.toDouble() ) + is ServerClearRectEvent -> renderer.clearRect( + x = x, + y = y, + width = width, + height = height, + ) + is ServerDrawImageEvent -> { val image = imageCacher.getImageData(imageId) diff --git a/projector-client-common/src/commonMain/kotlin/org/jetbrains/projector/client/common/canvas/buffering/DoubleBufferedRenderingSurface.kt b/projector-client-common/src/commonMain/kotlin/org/jetbrains/projector/client/common/canvas/buffering/DoubleBufferedRenderingSurface.kt index 434737f07..bf7f3043d 100644 --- a/projector-client-common/src/commonMain/kotlin/org/jetbrains/projector/client/common/canvas/buffering/DoubleBufferedRenderingSurface.kt +++ b/projector-client-common/src/commonMain/kotlin/org/jetbrains/projector/client/common/canvas/buffering/DoubleBufferedRenderingSurface.kt @@ -49,7 +49,11 @@ class DoubleBufferedRenderingSurface(bufferCanvasFactory: CanvasFactory, private // optimization: flush only if the buffer is changed if (buffer.changed) { buffer.resetChanged() - target.context2d.drawImage(buffer.imageSource, 0.0, 0.0) + target.context2d.apply { + // clear canvas so that semi-transparent parts won't stack on top of each other + clearRect(0.0, 0.0, buffer.width.toDouble(), buffer.height.toDouble()) + drawImage(buffer.imageSource, 0.0, 0.0) + } } } diff --git a/projector-client-common/src/jsMain/kotlin/org/jetbrains/projector/client/common/canvas/DomContext2d.kt b/projector-client-common/src/jsMain/kotlin/org/jetbrains/projector/client/common/canvas/DomContext2d.kt index 262685603..1ee0d3d09 100644 --- a/projector-client-common/src/jsMain/kotlin/org/jetbrains/projector/client/common/canvas/DomContext2d.kt +++ b/projector-client-common/src/jsMain/kotlin/org/jetbrains/projector/client/common/canvas/DomContext2d.kt @@ -95,7 +95,7 @@ internal class DomContext2d(private val myContext2d: CanvasRenderingContext2D) : CompositeOperationType.CLEAR, CompositeOperationType.DST, -> "source-over".also { - logger.info { "Missing implementation for $this, applying source-over" } + logger.info { "Missing implementation for $type, applying $it" } } } } diff --git a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/component/ClientComponent.kt b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/component/ClientComponent.kt index 06319a0c3..e01f69503 100644 --- a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/component/ClientComponent.kt +++ b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/component/ClientComponent.kt @@ -25,16 +25,24 @@ package org.jetbrains.projector.client.web.component import kotlinx.browser.document import org.w3c.dom.HTMLIFrameElement +import org.w3c.dom.events.Event +import org.w3c.dom.events.EventListener +import org.w3c.dom.events.MouseEvent +import org.w3c.dom.events.MouseEventInit abstract class ClientComponent( protected val id: Int, + private val onMouseMove: (Event) -> Unit, ) { + private val contentDocumentListeners = mutableMapOf() + val iFrame: HTMLIFrameElement = createIFrame(id) var windowId: Int? = null fun dispose() { + contentDocumentListeners.forEach { iFrame.contentDocument?.removeEventListener(it.key, it.value) } iFrame.remove() } @@ -60,6 +68,10 @@ abstract class ClientComponent( } contentDocument!!.oncontextmenu = { false } + + addEventListener("load", EventListener { + onContentChanged() + }) } protected fun setLinkProcessor(linkProcessor: (String) -> Unit) { @@ -92,6 +104,49 @@ abstract class ClientComponent( } } + private fun onContentChanged() { + contentDocumentListeners.forEach { iFrame.contentDocument!!.addEventListener(it.key, it.value) } + } + private fun getIFrameId(browserId: Int) = "${this::class.simpleName}$browserId" + init { + contentDocumentListeners["mousemove"] = EventListener { + + val mouseEventInit = with(it as MouseEvent) { + MouseEventInit( + screenX = screenX, + screenY = screenY, + clientX = clientX + iFrame.offsetLeft, + clientY = clientY + iFrame.offsetTop, + button = button, + buttons = buttons, + relatedTarget = relatedTarget, + region = region, + ctrlKey = ctrlKey, + shiftKey = shiftKey, + altKey = altKey, + metaKey = metaKey, + modifierAltGraph = getModifierState("AltGraph"), + modifierCapsLock = getModifierState("CapsLock"), + modifierFn = getModifierState("Fn"), + modifierFnLock = getModifierState("FnLock"), + modifierHyper = getModifierState("Hyper"), + modifierNumLock = getModifierState("NumLock"), + modifierScrollLock = getModifierState("ScrollLock"), + modifierSuper = getModifierState("Super"), + modifierSymbol = getModifierState("Symbol"), + modifierSymbolLock = getModifierState("SymbolLock"), + view = view, + detail = detail, + bubbles = bubbles, + cancelable = cancelable, + composed = composed, + ) + } + onMouseMove(MouseEvent(it.type, mouseEventInit)) + + } + onContentChanged() + } } diff --git a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/component/EmbeddedBrowserManager.kt b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/component/EmbeddedBrowserManager.kt index cf8336688..5ffdbb0cc 100644 --- a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/component/EmbeddedBrowserManager.kt +++ b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/component/EmbeddedBrowserManager.kt @@ -26,14 +26,17 @@ package org.jetbrains.projector.client.web.component import kotlinx.browser.document import org.jetbrains.projector.client.web.UriHandler import org.w3c.dom.HTMLScriptElement +import org.w3c.dom.events.Event +import org.w3c.dom.events.EventListener import org.w3c.dom.events.MouseEvent class EmbeddedBrowserManager( zIndexByWindowIdGetter: (Int) -> Int?, + private val onMouseMove: (Event) -> Unit, private val openInExternalBrowser: (String) -> Unit, ) : ClientComponentManager(zIndexByWindowIdGetter) { - class EmbeddedBrowserPanel(id: Int, private val openInExternalBrowser: (String) -> Unit) : ClientComponent(id) { + inner class EmbeddedBrowserPanel(id: Int, private val openInExternalBrowser: (String) -> Unit) : ClientComponent(id, onMouseMove) { var wasLoaded = false @@ -44,17 +47,15 @@ class EmbeddedBrowserManager( private val jsQueue = ArrayDeque() init { - iFrame.apply { - onload = { - setOpenLinksInExternalBrowser(openLinksInExternalBrowser, true) - - wasLoaded = true - while (jsQueue.isNotEmpty()) { - val code = jsQueue.removeFirst() - executeJsImpl(code) - } + iFrame.addEventListener("load", EventListener { + setOpenLinksInExternalBrowser(openLinksInExternalBrowser, true) + + wasLoaded = true + while (jsQueue.isNotEmpty()) { + val code = jsQueue.removeFirst() + executeJsImpl(code) } - } + }) } fun setHtml(html: String) { diff --git a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/component/MarkdownPanelManager.kt b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/component/MarkdownPanelManager.kt index 7bac52c79..89c222d58 100644 --- a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/component/MarkdownPanelManager.kt +++ b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/component/MarkdownPanelManager.kt @@ -25,19 +25,18 @@ package org.jetbrains.projector.client.web.component import kotlinx.dom.clear import org.jetbrains.projector.util.logging.Logger -import org.w3c.dom.HTMLElement -import org.w3c.dom.HTMLIFrameElement -import org.w3c.dom.Node -import org.w3c.dom.get +import org.w3c.dom.* +import org.w3c.dom.events.Event import org.w3c.dom.parsing.DOMParser import kotlin.math.absoluteValue class MarkdownPanelManager( zIndexByWindowIdGetter: (Int) -> Int?, + private val onMouseMove: (Event) -> Unit, private val openInExternalBrowser: (String) -> Unit, ) : ClientComponentManager(zIndexByWindowIdGetter) { - class MarkdownPanel(id: Int, openInExternalBrowser: (String) -> Unit) : ClientComponent(id) { + inner class MarkdownPanel(id: Int, openInExternalBrowser: (String) -> Unit) : ClientComponent(id, onMouseMove) { init { setLinkProcessor(openInExternalBrowser) diff --git a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/input/InputController.kt b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/input/InputController.kt index 87f02ce73..130d95584 100644 --- a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/input/InputController.kt +++ b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/input/InputController.kt @@ -75,7 +75,7 @@ class InputController( private var lastTouchX = 1 private var lastTouchY = 1 - private fun handleMouseMoveEvent(event: Event) { + fun handleMouseMoveEvent(event: Event) { require(event is MouseEvent) val topWindow = windowManager.getTopWindow(event.clientX, event.clientY) diff --git a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/protocol/ManualJsonToClientMessageDecoder.kt b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/protocol/ManualJsonToClientMessageDecoder.kt index 6e740835a..86acae3ed 100644 --- a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/protocol/ManualJsonToClientMessageDecoder.kt +++ b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/protocol/ManualJsonToClientMessageDecoder.kt @@ -158,6 +158,7 @@ object ManualJsonToClientMessageDecoder : ToClientMessageDecoder { "a" -> WindowType.WINDOW "b" -> WindowType.POPUP "c" -> WindowType.IDEA_WINDOW + "d" -> WindowType.FAKE_WINDOW else -> throw IllegalArgumentException("Unsupported window type: $this") } } @@ -286,6 +287,13 @@ object ManualJsonToClientMessageDecoder : ToClientMessageDecoder { "u" -> Flush + "v" -> ServerClearRectEvent( + content["a"] as Double, + content["b"] as Double, + content["c"] as Double, + content["d"] as Double, + ) + else -> throw IllegalArgumentException("Unsupported event type: ${JSON.stringify(this)}") } } diff --git a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/state/ClientState.kt b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/state/ClientState.kt index 993b7885a..a2cdaaecc 100644 --- a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/state/ClientState.kt +++ b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/state/ClientState.kt @@ -408,11 +408,11 @@ sealed class ClientState { true -> Typing.SpeculativeTyping(windowManager::getWindowCanvas) } - private val markdownPanelManager = MarkdownPanelManager(windowManager::getWindowZIndex) { link -> + private val markdownPanelManager = MarkdownPanelManager(windowManager::getWindowZIndex, inputController::handleMouseMoveEvent) { link -> stateMachine.fire(ClientAction.AddEvent(ClientOpenLinkEvent(link))) } - private val embeddedBrowserManager = EmbeddedBrowserManager(windowManager::getWindowZIndex) { link -> + private val embeddedBrowserManager = EmbeddedBrowserManager(windowManager::getWindowZIndex, inputController::handleMouseMoveEvent) { link -> stateMachine.fire(ClientAction.AddEvent(ClientOpenLinkEvent(link))) } diff --git a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/window/WebWindow.kt b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/window/WebWindow.kt index 5ca5f7d09..b12988ba8 100644 --- a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/window/WebWindow.kt +++ b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/window/WebWindow.kt @@ -71,7 +71,7 @@ class WebWindow(windowData: WindowData, private val stateMachine: ClientStateMac var isShowing: Boolean = false set(value) { header?.visible = value - border.visible = value + border?.visible = value if (field == value) { return @@ -81,13 +81,13 @@ class WebWindow(windowData: WindowData, private val stateMachine: ClientStateMac } //public only for speculative typing. - val canvas = createCanvas() + val canvas = createCanvas(windowData.id) private val renderingSurface = createRenderingSurface(canvas) private var header: WindowHeader? = null private var headerVerticalPosition: Double = 0.0 private var headerHeight: Double = 0.0 - private val border = WindowBorder(windowData.resizable) + private val border = if (windowData.windowType == WindowType.FAKE_WINDOW) null else WindowBorder(windowData.resizable) override val commandProcessor = SingleRenderingSurfaceProcessor(renderingSurface, imageCacher) @@ -106,7 +106,7 @@ class WebWindow(windowData: WindowData, private val stateMachine: ClientStateMac field = value canvas.style.zIndex = "$zIndex" header?.zIndex = zIndex - border.zIndex = zIndex - 1 + border?.zIndex = zIndex - 1 } } @@ -121,46 +121,46 @@ class WebWindow(windowData: WindowData, private val stateMachine: ClientStateMac init { applyBounds() - if (windowData.windowType == WindowType.IDEA_WINDOW || windowData.windowType == WindowType.POPUP) { - canvas.style.border = "none" - } - else if (windowData.windowType == WindowType.WINDOW) { - if (windowData.undecorated) { - canvas.style.border = ProjectorUI.borderStyle - } - else { - // If the window has a header on the host, its sizes are included in the window bounds. - // The client header is drawn above the window, outside its bounds. At the same time, - // the coordinates of the contents of the window come taking into account the size - // of the header. As a result, on client an empty space is obtained between header - // and the contents of the window. To get rid of this, we transfer the height of the system - // window header and if it > 0, we draw the heading not over the window but inside - // the window's bounds, filling in the empty space. - - header = WindowHeader(windowData.title) - header!!.undecorated = windowData.undecorated - header!!.onMove = ::onMove - header!!.onClose = ::onClose - - headerVerticalPosition = when (windowData.headerHeight) { - 0, null -> ProjectorUI.headerHeight - else -> 0.0 + when (windowData.windowType) { + WindowType.IDEA_WINDOW, WindowType.POPUP, WindowType.FAKE_WINDOW -> canvas.style.border = "none" + WindowType.WINDOW -> { + if (windowData.undecorated) { + canvas.style.border = ProjectorUI.borderStyle } - - headerHeight = when (windowData.headerHeight) { - 0, null -> ProjectorUI.headerHeight - else -> windowData.headerHeight!!.toDouble() + else { + // If the window has a header on the host, its sizes are included in the window bounds. + // The client header is drawn above the window, outside its bounds. At the same time, + // the coordinates of the contents of the window come taking into account the size + // of the header. As a result, on client an empty space is obtained between header + // and the contents of the window. To get rid of this, we transfer the height of the system + // window header and if it > 0, we draw the heading not over the window but inside + // the window's bounds, filling in the empty space. + + header = WindowHeader(windowData.title) + header!!.undecorated = windowData.undecorated + header!!.onMove = ::onMove + header!!.onClose = ::onClose + + headerVerticalPosition = when (windowData.headerHeight) { + 0, null -> ProjectorUI.headerHeight + else -> 0.0 + } + + headerHeight = when (windowData.headerHeight) { + 0, null -> ProjectorUI.headerHeight + else -> windowData.headerHeight!!.toDouble() + } + + canvas.style.borderBottom = ProjectorUI.borderStyle + canvas.style.borderLeft = ProjectorUI.borderStyle + canvas.style.borderRight = ProjectorUI.borderStyle + canvas.style.borderRadius = "0 0 ${ProjectorUI.borderRadius}px ${ProjectorUI.borderRadius}px" } - - canvas.style.borderBottom = ProjectorUI.borderStyle - canvas.style.borderLeft = ProjectorUI.borderStyle - canvas.style.borderRight = ProjectorUI.borderStyle - canvas.style.borderRadius = "0 0 ${ProjectorUI.borderRadius}px ${ProjectorUI.borderRadius}px" } } if (windowData.resizable) { - border.onResize = ::onResize + border?.onResize = ::onResize } } @@ -176,19 +176,19 @@ class WebWindow(windowData: WindowData, private val stateMachine: ClientStateMac } header?.lookAndFeelChanged() - border.lookAndFeelChanged() + border?.lookAndFeelChanged() } fun contains(x: Int, y: Int): Boolean { - return border.bounds.contains(x, y) + return border?.bounds?.contains(x, y) ?: bounds.contains(x, y) } fun onMouseDown(x: Int, y: Int): DragEventsInterceptor? { - return border.onMouseDown(x, y) ?: header?.onMouseDown(x, y) + return border?.onMouseDown(x, y) ?: header?.onMouseDown(x, y) } fun onMouseClick(x: Int, y: Int): DragEventsInterceptor? { - return border.onMouseClick(x, y) ?: header?.onMouseClick(x, y) + return border?.onMouseClick(x, y) ?: header?.onMouseClick(x, y) } private fun onResize(deltaX: Int, deltaY: Int, direction: ResizeDirection) { @@ -203,7 +203,8 @@ class WebWindow(windowData: WindowData, private val stateMachine: ClientStateMac stateMachine.fire(ClientAction.AddEvent(ClientWindowCloseEvent(id))) } - private fun createCanvas() = (document.createElement("canvas") as HTMLCanvasElement).apply { + private fun createCanvas(windowId: Int) = (document.createElement("canvas") as HTMLCanvasElement).apply { + id = "window${windowId}" style.position = "fixed" style.display = isShowing.toDisplayType() @@ -233,7 +234,7 @@ class WebWindow(windowData: WindowData, private val stateMachine: ClientStateMac header!!.draw() } - border.bounds = CommonRectangle( + border?.bounds = CommonRectangle( clientBounds.x, (bounds.y - headerVerticalPosition) * userScalingRatio, clientBounds.width, @@ -256,7 +257,7 @@ class WebWindow(windowData: WindowData, private val stateMachine: ClientStateMac fun dispose() { canvas.remove() - border.dispose() + border?.dispose() header?.dispose() } diff --git a/projector-common/src/commonMain/kotlin/org/jetbrains/projector/common/protocol/handshake/Constant.kt b/projector-common/src/commonMain/kotlin/org/jetbrains/projector/common/protocol/handshake/Constant.kt index 7654d8540..ee084fac4 100644 --- a/projector-common/src/commonMain/kotlin/org/jetbrains/projector/common/protocol/handshake/Constant.kt +++ b/projector-common/src/commonMain/kotlin/org/jetbrains/projector/common/protocol/handshake/Constant.kt @@ -64,4 +64,5 @@ val commonVersionList = listOf( 1741530029, 2040465192, 2043769487, + 901742065, ) diff --git a/projector-common/src/commonMain/kotlin/org/jetbrains/projector/common/protocol/toClient/ServerWindowEvent.kt b/projector-common/src/commonMain/kotlin/org/jetbrains/projector/common/protocol/toClient/ServerWindowEvent.kt index c860fc466..48c4ed186 100644 --- a/projector-common/src/commonMain/kotlin/org/jetbrains/projector/common/protocol/toClient/ServerWindowEvent.kt +++ b/projector-common/src/commonMain/kotlin/org/jetbrains/projector/common/protocol/toClient/ServerWindowEvent.kt @@ -251,3 +251,16 @@ data class ServerSetUnknownStrokeEvent( @Serializable @SerialName("u") object Flush : ServerWindowEvent() + +@Serializable +@SerialName("v") +data class ServerClearRectEvent( + @SerialName("a") + val x: Double, + @SerialName("b") + val y: Double, + @SerialName("c") + val width: Double, + @SerialName("d") + val height: Double, +) : ServerWindowPaintEvent() diff --git a/projector-common/src/commonMain/kotlin/org/jetbrains/projector/common/protocol/toClient/WindowData.kt b/projector-common/src/commonMain/kotlin/org/jetbrains/projector/common/protocol/toClient/WindowData.kt index 7986b549a..1e7f46f64 100644 --- a/projector-common/src/commonMain/kotlin/org/jetbrains/projector/common/protocol/toClient/WindowData.kt +++ b/projector-common/src/commonMain/kotlin/org/jetbrains/projector/common/protocol/toClient/WindowData.kt @@ -38,7 +38,10 @@ enum class WindowType { POPUP, @SerialName("c") - IDEA_WINDOW + IDEA_WINDOW, + + @SerialName("d") + FAKE_WINDOW, } // unlike WindowType, this is intended to always be the lowest inheritor of Window that's useful for clientside purposes diff --git a/projector-ij-common/build.gradle.kts b/projector-ij-common/build.gradle.kts index 98e3156e2..b10e4d5e5 100644 --- a/projector-ij-common/build.gradle.kts +++ b/projector-ij-common/build.gradle.kts @@ -48,6 +48,7 @@ dependencies { implementation("org.jsoup:jsoup:$jsoupVersion") compileOnly("com.jetbrains.intellij.platform:core-impl:$intellijPlatformVersion") + compileOnly("com.jetbrains.intellij.platform:ide-impl:$intellijPlatformVersion") compileOnly("com.jetbrains.intellij.platform:util-ui:$intellijPlatformVersion") compileOnly("org.jetbrains.intellij.deps.jcef:jcef:$intellijJcefVersion") diff --git a/projector-server-core/src/main/kotlin/org/jetbrains/projector/server/core/convert/toClient/Shift.kt b/projector-server-core/src/main/kotlin/org/jetbrains/projector/server/core/convert/toClient/Shift.kt index 250d3e261..456210193 100644 --- a/projector-server-core/src/main/kotlin/org/jetbrains/projector/server/core/convert/toClient/Shift.kt +++ b/projector-server-core/src/main/kotlin/org/jetbrains/projector/server/core/convert/toClient/Shift.kt @@ -25,18 +25,16 @@ package org.jetbrains.projector.server.core.convert.toClient import org.jetbrains.projector.common.protocol.data.CommonRectangle import org.jetbrains.projector.common.protocol.data.Point -import java.awt.Component +import java.awt.Rectangle import java.awt.Point as AwtPoint -public fun Component.shiftBounds(shift: AwtPoint): CommonRectangle { - return with(bounds) { - CommonRectangle( - (x - shift.x).toDouble(), - (y - shift.y).toDouble(), - width.toDouble(), - height.toDouble() - ) - } +public fun Rectangle.shift(shift: AwtPoint): CommonRectangle { + return CommonRectangle( + (x - shift.x).toDouble(), + (y - shift.y).toDouble(), + width.toDouble(), + height.toDouble() + ) } public fun AwtPoint.shift(shift: AwtPoint): Point {