Skip to content

Commit

Permalink
Merge pull request #49 from Danil0v3s/feat/recording
Browse files Browse the repository at this point in the history
[Feat] Add recording
  • Loading branch information
Danil0v3s authored Dec 29, 2024
2 parents 72c6354 + 74300af commit 921ea5a
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ object HardwareMonitorReader {
val buffer = getByteBuffer(packet.data, hardware * HARDWARE_SIZE + sensor * SENSOR_SIZE, HEADER_SIZE)
val hardwares = readHardware(buffer, hardware)
val sensors = readSensor(buffer, sensor)
_currentData = _currentData.copy(Hardwares = hardwares, Sensors = sensors)
_currentData = _currentData.copy(Hardwares = hardwares, Sensors = sensors, LastPollTime = System.currentTimeMillis())
_currentData
}

is Packet.PresentMonApps -> {
val appsCount = getByteBuffer(packet.data, LENGTH_SIZE, 0).short
val buffer = getByteBuffer(packet.data, appsCount.toInt() * NAME_SIZE, LENGTH_SIZE)
val apps = listOf("Auto") + readPresentMonApps(buffer, appsCount)
_currentData = _currentData.copy(PresentMonApps = apps)
_currentData = _currentData.copy(PresentMonApps = apps, LastPollTime = System.currentTimeMillis())
_currentData
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@ import com.github.kwhat.jnativehook.GlobalScreen
import com.github.kwhat.jnativehook.keyboard.NativeKeyEvent
import com.github.kwhat.jnativehook.keyboard.NativeKeyListener
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.Channel.Factory.CONFLATED
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.receiveAsFlow

sealed class KeyboardEvent {
data object ToggleOverlay : KeyboardEvent()
data object ToggleRecording : KeyboardEvent()
}

internal object KeyboardManager {

private val _channel = Channel<KeyboardEvent>()
private val _channel = Channel<KeyboardEvent>(CONFLATED)
val events = _channel.receiveAsFlow()

fun filter(event: KeyboardEvent) = events.filterIsInstance(event::class)
Expand All @@ -25,11 +27,15 @@ internal object KeyboardManager {
override fun nativeKeyReleased(nativeEvent: NativeKeyEvent) {
val isCtrl = nativeEvent.modifiers.and(NativeKeyEvent.CTRL_MASK) > 0
val isAlt = nativeEvent.modifiers.and(NativeKeyEvent.VC_ALT) > 0
val isF10 = nativeEvent.keyCode == NativeKeyEvent.VC_F10

if (isCtrl && isAlt && isF10) {
_channel.trySend(KeyboardEvent.ToggleOverlay)
if (!isCtrl && !isAlt) return

val event = when (nativeEvent.keyCode) {
NativeKeyEvent.VC_F10 -> KeyboardEvent.ToggleOverlay
NativeKeyEvent.VC_F11 -> KeyboardEvent.ToggleRecording
else -> null
}
event?.let { _channel.trySend(it) }
}
})
} catch (e: Throwable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ fun main(vararg args: String) = singleInstance(args) {
ProcessManager.stop()
})
} else {
KeyboardManager.registerKeyboardHook()
}
KeyboardManager.registerKeyboardHook()

if (!ApplicationParams.isAutostart) {
ProcessManager.start()
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package app.cleanmeter.target.desktop.ui.overlay

import androidx.lifecycle.ViewModel
import app.cleanmeter.core.common.hardwaremonitor.HardwareMonitorData
import app.cleanmeter.target.desktop.data.ObserveHardwareReadings
import app.cleanmeter.core.os.hardwaremonitor.HardwareMonitorReader
import app.cleanmeter.target.desktop.data.OverlaySettingsRepository
import app.cleanmeter.target.desktop.model.OverlaySettings
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -40,9 +40,11 @@ class OverlayViewModel : ViewModel() {

private fun observeHwInfo() {
CoroutineScope(Dispatchers.IO).launch {
ObserveHardwareReadings.data.collectLatest { hwInfoData ->
_state.update { it.copy(hardwareData = hwInfoData) }
}
HardwareMonitorReader
.currentData
.collectLatest { hwInfoData ->
_state.update { it.copy(hardwareData = hwInfoData) }
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,33 @@ package app.cleanmeter.target.desktop.ui.settings
import androidx.compose.ui.unit.IntOffset
import androidx.lifecycle.ViewModel
import app.cleanmeter.core.common.hardwaremonitor.HardwareMonitorData
import app.cleanmeter.core.os.hardwaremonitor.HardwareMonitorReader
import app.cleanmeter.core.os.hardwaremonitor.Packet
import app.cleanmeter.core.os.hardwaremonitor.SocketClient
import app.cleanmeter.target.desktop.data.ObserveHardwareReadings
import app.cleanmeter.target.desktop.KeyboardEvent
import app.cleanmeter.target.desktop.KeyboardManager
import app.cleanmeter.target.desktop.data.OverlaySettingsRepository
import app.cleanmeter.target.desktop.model.OverlaySettings
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.io.File
import java.io.Writer

data class SettingsState(
val overlaySettings: OverlaySettings? = null,
val hardwareData: HardwareMonitorData? = null
val hardwareData: HardwareMonitorData? = null,
val isRecording: Boolean = false,
)

sealed class SettingsEvent {
Expand All @@ -43,9 +54,13 @@ class SettingsViewModel : ViewModel() {
val state: Flow<SettingsState>
get() = _state

private val dataHistory = emptyList<HardwareMonitorData>().toMutableList()

init {
observeOverlaySettings()
observeHwInfo()
observeData()
observeRecordingHotkey()
observeRecordingState()
}

private fun observeOverlaySettings() {
Expand All @@ -58,11 +73,46 @@ class SettingsViewModel : ViewModel() {
}
}

private fun observeHwInfo() {
private fun observeData() {
CoroutineScope(Dispatchers.IO).launch {
ObserveHardwareReadings.data.collectLatest { hwInfoData ->
_state.update { it.copy(hardwareData = hwInfoData) }
}
HardwareMonitorReader
.currentData
.collectLatest { hwInfoData ->
_state.update { it.copy(hardwareData = hwInfoData) }
}
}
}

private fun observeRecordingHotkey() {
CoroutineScope(Dispatchers.Default).launch {
KeyboardManager
.filter(KeyboardEvent.ToggleRecording)
.collectLatest {
println("Toggle recording ${_state.value.isRecording}")
_state.update { it.copy(isRecording = !it.isRecording) }
}
}
}

private fun observeRecordingState() {
CoroutineScope(Dispatchers.Default).launch {
_state
.collectLatest { state ->
when {
state.isRecording && state.hardwareData != null -> {
dataHistory.add(state.hardwareData)
return@collectLatest
}
!state.isRecording && dataHistory.isNotEmpty() -> {
File("cleanmeter.recording.${System.currentTimeMillis()}.json").printWriter().use {
it.append(Json.encodeToString(dataHistory))
dataHistory.clear()
}
}
dataHistory.isNotEmpty() -> dataHistory.clear()
else -> Unit
}
}
}
}

Expand Down Expand Up @@ -145,6 +195,7 @@ class SettingsViewModel : ViewModel() {
)
)
}

SensorType.Framerate -> settingsState.overlaySettings
SensorType.Frametime -> settingsState.overlaySettings
SensorType.TotalVramUsed -> settingsState.overlaySettings
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
package app.cleanmeter.target.desktop.ui.settings.tabs

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.ClickableText
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Info
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ParagraphStyle
import androidx.compose.ui.text.SpanStyle
Expand All @@ -30,6 +38,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import app.cleanmeter.core.designsystem.LocalColorScheme
import app.cleanmeter.core.designsystem.LocalTypography
import app.cleanmeter.target.desktop.ui.components.HotKeySymbol
import app.cleanmeter.target.desktop.ui.components.section.CollapsibleSection

@Composable
Expand Down Expand Up @@ -101,6 +110,33 @@ internal fun HelpSettingsUi() {
}
)
}

CollapsibleSection(title = "HOTKEYS") {
Hotkey(label = "Toggle the overlay", "F10")
Hotkey(label = "Toggle data recording", "F11")
}
}
}

@Composable
private fun Hotkey(label: String, key: String) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(
text = label,
color = LocalColorScheme.current.text.heading,
style = LocalTypography.current.labelLMedium,
modifier = Modifier.wrapContentHeight(),
)
}
HotKeySymbol(listOf("Ctrl", "Alt", key))
}
}

Expand Down

0 comments on commit 921ea5a

Please sign in to comment.