Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(sessionprojector): add experimental support for resizing, display configuration change #8

Merged
merged 8 commits into from
Oct 6, 2022
8 changes: 8 additions & 0 deletions sessionprojector/sessionprojector.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
85721156283B607000C36D5F /* UlalacaCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 85721155283B607000C36D5F /* UlalacaCore.framework */; };
85721157283B607000C36D5F /* UlalacaCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 85721155283B607000C36D5F /* UlalacaCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
F4EBD136805519C3F8D8314A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EBD3D9F922ED6C0BBA9607 /* AppDelegate.swift */; };
F4EBD18EFC3BFC5565288F9C /* CGRect+toULIPCRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EBD846347B06D7E7B73FC1 /* CGRect+toULIPCRect.swift */; };
F4EBD3107D60426E249C21FD /* ULIPCRect+scale.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EBDCBA0826CA18FB1E77CF /* ULIPCRect+scale.swift */; };
F4EBD35869E7EEC01C4CE48D /* SessionManagerClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EBDFDEDFC5A96EE4B3F91C /* SessionManagerClient.swift */; };
F4EBD455F06C38A5ABA1EA20 /* ProjectionSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EBDEADE7680E1D1466830F /* ProjectionSession.swift */; };
F4EBD517D25C43D7397EA0FD /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F4EBD2D2B450575F0A669D2E /* MainMenu.xib */; };
Expand Down Expand Up @@ -47,8 +49,10 @@
F4EBD55AD46E2D55483DA879 /* AVFScreenRecorder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AVFScreenRecorder.swift; sourceTree = "<group>"; };
F4EBD5938B6F75A2117975F5 /* pl.unstabler.ulalaca.sessionprojector.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = pl.unstabler.ulalaca.sessionprojector.plist; sourceTree = "<group>"; };
F4EBD72F7A1676E55BB21493 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
F4EBD846347B06D7E7B73FC1 /* CGRect+toULIPCRect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGRect+toULIPCRect.swift"; sourceTree = "<group>"; };
F4EBDB589E76D41675D27BF0 /* ProjectionServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProjectionServer.swift; sourceTree = "<group>"; };
F4EBDB6CE8B3BF020BD4FE81 /* sessionprojector.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = sessionprojector.entitlements; sourceTree = "<group>"; };
F4EBDCBA0826CA18FB1E77CF /* ULIPCRect+scale.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ULIPCRect+scale.swift"; sourceTree = "<group>"; };
F4EBDEADE7680E1D1466830F /* ProjectionSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProjectionSession.swift; sourceTree = "<group>"; };
F4EBDEC6CB9E4322333D6C24 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
F4EBDFDEDFC5A96EE4B3F91C /* SessionManagerClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionManagerClient.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -91,6 +95,8 @@
F4EBDFDEDFC5A96EE4B3F91C /* SessionManagerClient.swift */,
F4EBD0BFDAED6400792ABDFA /* SCScreenRecorder.swift */,
F4EBD55AD46E2D55483DA879 /* AVFScreenRecorder.swift */,
F4EBDCBA0826CA18FB1E77CF /* ULIPCRect+scale.swift */,
F4EBD846347B06D7E7B73FC1 /* CGRect+toULIPCRect.swift */,
);
path = sessionprojector;
sourceTree = "<group>";
Expand Down Expand Up @@ -201,6 +207,8 @@
F4EBD35869E7EEC01C4CE48D /* SessionManagerClient.swift in Sources */,
F4EBDCD57BF1530790AC739E /* SCScreenRecorder.swift in Sources */,
F4EBD732593CF09DECD7B40A /* AVFScreenRecorder.swift in Sources */,
F4EBD3107D60426E249C21FD /* ULIPCRect+scale.swift in Sources */,
F4EBD18EFC3BFC5565288F9C /* CGRect+toULIPCRect.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
16 changes: 16 additions & 0 deletions sessionprojector/sessionprojector/CGRect+toULIPCRect.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// Created by Gyuhwan Park on 2022/08/01.
//

import Foundation

extension CGRect {
func toULIPCRect() -> ULIPCRect {
return ULIPCRect(
x: Int16(origin.x),
y: Int16(origin.y),
width: Int16(size.width),
height: Int16(size.height)
)
}
}
17 changes: 3 additions & 14 deletions sessionprojector/sessionprojector/EventInjector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,13 @@ class EventInjector {
}

func prepare() throws {
guard
/* let eventTap = CGEvent.tapCreate(
tap: .cgSessionEventTap,
place: .headInsertEventTap,
options: .defaultTap,
eventsOfInterest: UInt64(CGEventType.null.rawValue),
callback: { (proxy, type, event, refcon) in
return Unmanaged.passUnretained(event)
},
userInfo: nil
), */ let eventSource = CGEventSource(
guard let eventSource = CGEventSource(
stateID: .combinedSessionState
)
else {
throw EventInjectorError.initializationError
}

// self.eventTap = eventTap
self.eventSource = eventSource
}

Expand Down Expand Up @@ -87,10 +76,10 @@ class EventInjector {
cgEvent.post(tap: .cgSessionEventTap)
}

func post(mouseMoveEvent event: ULIPCMouseMoveEvent) {
func post(mouseMoveEvent event: ULIPCMouseMoveEvent, scaleX: Double = 1.0, scaleY: Double = 1.0) {
var mouseType: CGEventType = .mouseMoved
var mouseButton: CGMouseButton = .left
let position = CGPoint(x: Int(event.x), y: Int(event.y))
let position = CGPoint(x: Int(Double(event.x) * scaleX), y: Int(Double(event.y) * scaleY))

if (mouseDownState & EventInjector.MOUSE_DOWN_STATE_LEFT > 0) {
mouseType = .leftMouseDragged
Expand Down
70 changes: 51 additions & 19 deletions sessionprojector/sessionprojector/ProjectionSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,28 @@ enum ProjectionSessionError: Error {
case socketReadError
}



class ProjectionSession {
private let logger: ULLogger

public let socket: MMUnixSocketConnection
public var eventInjector: EventInjector? = nil

public let mainDisplayId = CGMainDisplayID()
public let serialQueue = DispatchQueue(label: "ProjectionSession")
public let updateLock = DispatchSemaphore(value: 1)

private(set) public var messageId: UInt64 = 1;
private(set) public var suppressOutput: Bool = true

public var eventInjector: EventInjector? = nil
private(set) public var screenResolution: CGSize = CGSize(width: 0, height: 0)
private(set) public var mainViewport: ViewportInfo?

init(_ socket: MMUnixSocketConnection) {
self.socket = socket

self.logger = createLogger("ProjectionSession (fd \(self.socket.descriptor()))")
}

func startSession(errorHandler: @escaping (Error) -> Void) {
Expand All @@ -47,7 +56,11 @@ class ProjectionSession {
eventInjector?.post(keyEvent: try socket.readCStruct(ULIPCKeyboardEvent.self))
break
case TYPE_EVENT_MOUSE_MOVE:
eventInjector?.post(mouseMoveEvent: try socket.readCStruct(ULIPCMouseMoveEvent.self))
eventInjector?.post(
mouseMoveEvent: try socket.readCStruct(ULIPCMouseMoveEvent.self),
scaleX: Double(screenResolution.width) / Double(mainViewport!.width),
scaleY: Double(screenResolution.height) / Double(mainViewport!.height)
)
break
case TYPE_EVENT_MOUSE_BUTTON:
eventInjector?.post(mouseButtonEvent: try socket.readCStruct(ULIPCMouseButtonEvent.self))
Expand All @@ -56,13 +69,36 @@ class ProjectionSession {
eventInjector?.post(mouseWheelEvent: try socket.readCStruct(ULIPCMouseWheelEvent.self))
break

case TYPE_PROJECTION_START:
try socket.readCStruct(ULIPCProjectionStart.self)
suppressOutput = false
break
case TYPE_PROJECTION_STOP:
try socket.readCStruct(ULIPCProjectionStop.self)
suppressOutput = true
break

case TYPE_PROJECTION_SET_VIEWPORT:
self.setViewport(with: try socket.readCStruct(ULIPCProjectionSetViewport.self))
break

default:
let buffer = UnsafeMutableRawPointer.allocate(byteCount: Int(header.length), alignment: 0)
try socket.readEx(buffer, size: Int(header.length))
}
}
}


private func setViewport(with message: ULIPCProjectionSetViewport) {
if (message.monitorId != 0) {
logger.debug("multi display layout is not supported yet")
return
}

mainViewport = ViewportInfo(width: message.width, height: message.height)
}


private func writeMessage<T>(_ message: T, type: UInt16) {
let messageLength = MemoryLayout.size(ofValue: message)
let header = ULIPCHeader(
Expand All @@ -75,7 +111,7 @@ class ProjectionSession {

socket.write(withUnsafePointer(to: header) { $0 }, size: MemoryLayout.size(ofValue: header))
socket.write(withUnsafePointer(to: message) { $0 }, size: messageLength)

messageId += 1
}

Expand All @@ -88,15 +124,13 @@ extension ProjectionSession: ScreenUpdateSubscriber {

func screenUpdated(where rect: CGRect) {
self.serialQueue.sync {
let sx = mainViewport?.scaleX(Int(screenResolution.width)) ?? 1.0
let sy = mainViewport?.scaleY(Int(screenResolution.height)) ?? 1.0

self.writeMessage(
ULIPCScreenUpdateNotify(
ULIPCScreenUpdateNotify(
type: SCREEN_UPDATE_NOTIFY_TYPE_PARTIAL,
rect: ULIPCRect(
x: Int16(rect.origin.x),
y: Int16(rect.origin.y),
width: Int16(rect.size.width),
height: Int16(rect.size.height)
)
rect: rect.toULIPCRect().scale(x: sx, y: sy)
),
type: TYPE_SCREEN_UPDATE_NOTIFY
)
Expand All @@ -110,13 +144,11 @@ extension ProjectionSession: ScreenUpdateSubscriber {
let pointer = CFDataGetBytePtr(rawData)!
let length = CFDataGetLength(rawData)

let sx = mainViewport?.scaleX(Int(screenResolution.width)) ?? 1.0
let sy = mainViewport?.scaleY(Int(screenResolution.height)) ?? 1.0

let message = ULIPCScreenUpdateCommit(
screenRect: ULIPCRect(
x: Int16(rect.origin.x),
y: Int16(rect.origin.y),
width: Int16(rect.size.width),
height: Int16(rect.size.height)
),
screenRect: rect.toULIPCRect().scale(x: sx, y: sy),
bitmapLength: UInt64(length)
)

Expand All @@ -125,8 +157,8 @@ extension ProjectionSession: ScreenUpdateSubscriber {
}
}

func screenResolutionChanged(to resolution: (Int, Int)) {

func screenResolutionChanged(to resolution: CGSize) {
self.screenResolution = resolution
}

}
Loading