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

✨ clipboard listening on mac side #6

Merged
merged 2 commits into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import org.jetbrains.compose.resources.painterResource
@Composable
fun ClipeveryApp(clipboard: AbstractClipboard, copyText: MutableState<String>) {
MaterialTheme {
val pid: Long = ProcessHandle.current().pid()
var showImage by remember { mutableStateOf(false) }
var start by remember { mutableStateOf(true) }
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
Expand All @@ -39,7 +40,7 @@ fun ClipeveryApp(clipboard: AbstractClipboard, copyText: MutableState<String>) {
}
start = !start
}) {
Text(start.toString())
Text(start.toString() + " " + pid)
}
AnimatedVisibility(showImage) {
Image(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
package com.clipevery.clip
interface AbstractClipboard: Runnable, ClipboardMonitor {

import java.awt.datatransfer.Transferable
import java.util.function.Consumer

interface AbstractClipboard: Runnable, ClipboardMonitor {

val clipConsumer: Consumer<Transferable>

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,4 @@ interface ClipboardMonitor {
fun start()

fun stop()

fun onChange(event: ClipboardEvent?)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.clipevery

import com.clipevery.clip.AbstractClipboard
import com.clipevery.macos.MacosClipboard
import com.clipevery.platform.currentPlatform
import com.clipevery.windows.WindowsClipboard
import java.awt.datatransfer.Transferable
import java.util.function.Consumer

fun getClipboard(clipConsumer: Consumer<Transferable>): AbstractClipboard {
val platform = currentPlatform()
return if (platform.name == "Macos") {
MacosClipboard(clipConsumer)
} else if (platform.name == "Windows") {
WindowsClipboard(clipConsumer)
} else {
throw Exception("Unknown platform: ${platform.name}")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.clipevery.macos

import com.clipevery.clip.AbstractClipboard
import com.clipevery.macos.api.MacClipboard
import java.awt.Toolkit
import java.awt.datatransfer.Clipboard
import java.awt.datatransfer.Transferable
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
import java.util.function.Consumer


class MacosClipboard
(override val clipConsumer: Consumer<Transferable>) : AbstractClipboard {

private var executor: ScheduledExecutorService? = null

private var changeCount = 0

private var systemClipboard: Clipboard = Toolkit.getDefaultToolkit().systemClipboard

override fun run() {
try {
MacClipboard.INSTANCE.clipboardChangeCount.let { currentChangeCount ->
if (changeCount == currentChangeCount) {
return
}
changeCount = currentChangeCount
val contents: Transferable? = systemClipboard.getContents(null)
contents?.let {
clipConsumer.accept(it)
}
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}

override fun start() {
if (executor?.isShutdown != false) {
executor = Executors.newScheduledThreadPool(2) { r -> Thread(r, "Clipboard Monitor") }
}
executor?.scheduleAtFixedRate(this, 0, 300, TimeUnit.MILLISECONDS)
}

override fun stop() {
executor?.let {
it.shutdown()

try {
if (!it.awaitTermination(600, TimeUnit.MICROSECONDS)) {
it.shutdownNow()
if (!it.awaitTermination(600, TimeUnit.MICROSECONDS)) {
println("task did not terminate")
}
}
println("stop ")
} catch (ie: InterruptedException) {
Thread.currentThread().interrupt()
it.shutdownNow()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.clipevery.macos.api

import com.sun.jna.Library
import com.sun.jna.Native


interface MacClipboard : Library {
val clipboardChangeCount: Int

companion object {
val INSTANCE: MacClipboard = Native.load("ClipboardHelper", MacClipboard::class.java)
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.clipevery.windows

import androidx.compose.runtime.MutableState
import com.clipevery.clip.AbstractClipboard
import com.clipevery.clip.ClipboardEvent
import com.clipevery.windows.api.User32
import com.sun.jna.Pointer
import com.sun.jna.platform.win32.Kernel32
Expand All @@ -12,17 +10,17 @@ import com.sun.jna.platform.win32.WinDef.WPARAM
import com.sun.jna.platform.win32.WinUser.MSG
import java.awt.Toolkit
import java.awt.datatransfer.Clipboard
import java.awt.datatransfer.DataFlavor
import java.awt.datatransfer.Transferable
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.function.Consumer


class WindowsClipboard
(private val copyText: MutableState<String>) : AbstractClipboard, User32.WNDPROC {
(override val clipConsumer: Consumer<Transferable>) : AbstractClipboard, User32.WNDPROC {

private var systemClipboard: Clipboard = Toolkit.getDefaultToolkit().getSystemClipboard()
private var systemClipboard: Clipboard = Toolkit.getDefaultToolkit().systemClipboard

private var executor: ExecutorService? = null
private var viewer: HWND? = null
Expand Down Expand Up @@ -91,17 +89,10 @@ class WindowsClipboard
}
}

override fun onChange(event: ClipboardEvent?) {
private fun onChange() {
val contents: Transferable? = systemClipboard.getContents(null)
contents?.let {
if (it.isDataFlavorSupported(DataFlavor.stringFlavor)) {
try {
copyText.value = contents.getTransferData(DataFlavor.stringFlavor).toString()
println(contents.getTransferData(DataFlavor.stringFlavor))
} catch (e: Exception) {
e.printStackTrace()
}
}
clipConsumer.accept(it)
}
}

Expand All @@ -122,7 +113,7 @@ class WindowsClipboard

User32.WM_DRAWCLIPBOARD -> {
try {
onChange(ClipboardEvent(this))
onChange()
} finally {
User32.INSTANCE.SendMessage(nextViewer, uMsg, uParam, lParam)
}
Expand Down
36 changes: 30 additions & 6 deletions composeApp/src/desktopMain/kotlin/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,45 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import com.clipevery.ClipeveryApp
import com.clipevery.windows.WindowsClipboard
import com.clipevery.getClipboard
import java.awt.datatransfer.DataFlavor
import java.awt.datatransfer.Transferable
import java.util.function.Consumer


fun main() = application {
val copyText = remember { mutableStateOf("Hello World!") }
val windowsClipboard = WindowsClipboard(copyText)
windowsClipboard.start()
val consumer = Consumer<Transferable> {
if (it.isDataFlavorSupported(DataFlavor.stringFlavor)) {
try {
copyText.value = it.getTransferData(DataFlavor.stringFlavor).toString()
println(it.getTransferData(DataFlavor.stringFlavor))
} catch (e: Exception) {
e.printStackTrace()
}
}
}
val clipboard = getClipboard(consumer)
clipboard.start()
Window(onCloseRequest = ::exitApplication) {
ClipeveryApp(windowsClipboard, copyText)
ClipeveryApp(clipboard, copyText)
}
}

@Preview
@Composable
fun AppDesktopPreview() {
val copyText = remember { mutableStateOf("Hello World!") }
val windowsClipboard = WindowsClipboard(copyText)
ClipeveryApp(windowsClipboard, copyText)
val consumer = Consumer<Transferable> {
if (it.isDataFlavorSupported(DataFlavor.stringFlavor)) {
try {
copyText.value = it.getTransferData(DataFlavor.stringFlavor).toString()
println(it.getTransferData(DataFlavor.stringFlavor))
} catch (e: Exception) {
e.printStackTrace()
}
}
}
val clipboard = getClipboard(consumer)
ClipeveryApp(clipboard, copyText)
}
Binary file not shown.
Loading