Skip to content

Commit

Permalink
🔨 Use coroutines instead of java thread-style pasteboard listening (#381
Browse files Browse the repository at this point in the history
)
  • Loading branch information
guiyanakuang authored Feb 25, 2024
1 parent 0197d85 commit c0904c6
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ interface ClipboardMonitor {
fun start()

fun stop()

fun run()
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.clipevery.clip

interface ClipboardService: Runnable, ClipboardMonitor {
interface ClipboardService: ClipboardMonitor {

val clipConsumer: TransferableConsumer

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,62 +3,57 @@ package com.clipevery.os.macos
import com.clipevery.clip.ClipboardService
import com.clipevery.clip.TransferableConsumer
import com.clipevery.os.macos.api.MacosApi
import com.clipevery.utils.cpuDispatcher
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
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

class MacosClipboardService
(override val clipConsumer: TransferableConsumer) : ClipboardService {
class MacosClipboardService(override val clipConsumer: TransferableConsumer): ClipboardService {

private var executor: ScheduledExecutorService? = null
private val logger = KotlinLogging.logger {}

private var changeCount = 0

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

override fun run() {
try {
MacosApi.INSTANCE.getClipboardChangeCount().let { currentChangeCount ->
if (changeCount == currentChangeCount) {
return
}
changeCount = currentChangeCount
val contents: Transferable? = systemClipboard.getContents(null)
contents?.let {
clipConsumer.consume(it)
private var job: Job? = null

override fun run(): Unit = runBlocking {
launch {
while (isActive) {
try {
MacosApi.INSTANCE.getClipboardChangeCount().let { currentChangeCount ->
if (changeCount != currentChangeCount) {
changeCount = currentChangeCount
val contents = systemClipboard.getContents(null)
contents?.let {
clipConsumer.consume(it)
}
}
}
} catch (e: Exception) {
logger.error(e) { "Failed to consume transferable" }
}
delay(300L)
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}

override fun start() {
if (executor?.isShutdown != false) {
executor = Executors.newScheduledThreadPool(2) { r -> Thread(r, "Clipboard Monitor") }
if (job?.isActive != true) {
job = CoroutineScope(cpuDispatcher).launch {
run()
}
}
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()
}
}
job?.cancel()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,29 @@ package com.clipevery.os.windows

import com.clipevery.clip.ClipboardService
import com.clipevery.clip.TransferableConsumer
import com.clipevery.platform.currentPlatform
import com.clipevery.os.windows.api.User32
import com.clipevery.platform.currentPlatform
import com.clipevery.utils.ioDispatcher
import com.sun.jna.Pointer
import com.sun.jna.platform.win32.Kernel32
import com.sun.jna.platform.win32.WinDef.HWND
import com.sun.jna.platform.win32.WinDef.LPARAM
import com.sun.jna.platform.win32.WinDef.WPARAM
import com.sun.jna.platform.win32.WinUser.MSG
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import java.awt.Toolkit
import java.awt.datatransfer.Clipboard
import java.awt.datatransfer.Transferable
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit


class WindowsClipboardService
(override val clipConsumer: TransferableConsumer) : ClipboardService, User32.WNDPROC {

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

private var executor: ExecutorService? = null
private var job: Job? = null
private var viewer: HWND? = null
private var nextViewer: HWND? = null
private val event = Kernel32.INSTANCE.CreateEvent(
Expand Down Expand Up @@ -69,30 +70,16 @@ class WindowsClipboardService
}

override fun start() {
if (executor?.isShutdown != false) {
executor = Executors.newSingleThreadExecutor { r -> Thread(r, "Clipboard Monitor") }
if (job?.isActive != true) {
job = CoroutineScope(ioDispatcher).launch {
run()
}
}
executor?.execute(this)
}

override fun stop() {
executor?.let {
Kernel32.INSTANCE.SetEvent(event)
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()
}
}
Kernel32.INSTANCE.SetEvent(event)
job?.cancel()
}

private fun onChange() {
Expand Down

0 comments on commit c0904c6

Please sign in to comment.