package service import java.util.concurrent.TimeUnit import org.slf4j.LoggerFactory import ch.qos.logback.classic.Level import ch.qos.logback.classic.Logger import java.awt.MouseInfo import java.awt.Robot import java.time.LocalTime import java.util.concurrent.Executors import java.util.regex.Pattern object SystemSleepPreventer { private var process: Process? = null private val robot = Robot() private val scheduler = Executors.newSingleThreadScheduledExecutor() // 작동 시간 설정 private val startTime = LocalTime.of(8, 30) private val endTime = LocalTime.of(16, 0) fun checkAndRequestAccessibility() { if (!hasAccessibilityPermission()) { println("⚠️ [System] 접근성 권한이 없습니다. 설정창을 엽니다.") openAccessibilitySettings() } else { println("✅ [System] 접근성 권한이 확인되었습니다.") } } /** * 실제로 가벼운 이벤트를 발생시켜 권한 유무를 확인하는 함수 */ private fun hasAccessibilityPermission(): Boolean { return try { // System Events에 이름을 묻는 아주 가벼운 명령을 실행합니다. val process = Runtime.getRuntime().exec( arrayOf("osascript", "-e", "tell application \"System Events\" to get name") ) // 권한이 없으면 팝업이 뜨며 대기할 수 있으므로, 아주 짧은 타임아웃을 줍니다. val exited = process.waitFor(1500, java.util.concurrent.TimeUnit.MILLISECONDS) if (!exited) { // 타임아웃 발생 시, 시스템이 권한 승인 팝업을 띄우고 대기 중일 확률이 높습니다. process.destroyForcibly() return false } // 에러 스트림을 읽어 권한 거부 관련 메시지가 있는지 확인합니다. val errorStream = process.errorStream.bufferedReader().readText() val isDenied = errorStream.contains("not allowed") || process.exitValue() != 0 !isDenied } catch (e: Exception) { println("⚠️ [Permission] 체크 중 오류 발생: ${e.message}") false } } private fun openAccessibilitySettings() { try { Runtime.getRuntime().exec(arrayOf( "open", "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" )) } catch (e: Exception) { e.printStackTrace() } } /** * 맥의 절전 모드 및 디스플레이 취침을 방지하는 명령 실행 */ fun start() { val os = System.getProperty("os.name").lowercase() val arch = System.getProperty("os.arch").lowercase() val isWin = os.contains("win") val root = LoggerFactory.getLogger("Exposed") as Logger root.level = Level.ERROR if (!isWin) { checkAndRequestAccessibility() } if (process?.isAlive == true) return if (!isWin) { // try { // // -i: 시스템 절전 방지, -d: 디스플레이 취침 방지, -m: 디스크 유휴 상태 방지 // val command = listOf("caffeinate", "-i", "-d", "-m") // process = ProcessBuilder(command).start() // println("☕ [System] caffeinate 실행됨: 앱이 켜져 있는 동안 절전 모드가 방지됩니다.") // } catch (e: Exception) { // println("⚠️ [System] caffeinate 실행 실패: ${e.message}") // } } start2() } fun start2() { println("🚀 화면 잠금 방지 프로그램이 시작되었습니다. (작동 시간: $startTime ~ $endTime)") // 1분(60초)마다 체크 scheduler.scheduleAtFixedRate({ if (isWorkingTime()) { moveMouseSlightly() } else { println("💤 현재는 휴식 시간입니다. (${LocalTime.now().withNano(0)})") } }, 0, 60 * 2, TimeUnit.SECONDS) } private fun isWorkingTime(): Boolean { val now = LocalTime.now() // 시작 시간 이후 AND 종료 시간 이전인지 확인 return now.isAfter(startTime) && now.isBefore(endTime) } private fun moveMouseSlightly() { try { val pointer = MouseInfo.getPointerInfo().location val x = pointer.x val y = pointer.y // 현재 위치에서 1픽셀씩 이동했다가 복귀 (사용자 방해 최소화) robot.mouseMove(x + 1, y + 1) robot.mouseMove(x, y) println("🖱️ [${LocalTime.now().withNano(0)}] 마우스 신호를 보냈습니다.") robot.keyPress(java.awt.event.KeyEvent.VK_SHIFT) robot.keyRelease(java.awt.event.KeyEvent.VK_SHIFT) println("🖱️ [${LocalTime.now().withNano(0)}] 키보드 신호를 보냈습니다.") } catch (e: Exception) { println("⚠️ 마우스 제어 실패: ${e.message}") } } private val osName = System.getProperty("os.name").lowercase() // 설정 시간 private val dimTime = LocalTime.of(16, 0) // 오후 4시 이후 최저 밝기 fun start3() { scheduler.scheduleAtFixedRate({ val now = LocalTime.now() // 16:00 이후라면 밝기를 낮춤 if (now.isAfter(dimTime) || now.isBefore(LocalTime.of(8, 30))) { setBrightness(0) } else { setBrightness(100) // 업무 시간에는 다시 밝게 (80%) } }, 0, 10, TimeUnit.MINUTES) // 10분마다 체크 } private fun setBrightness(level: Int) { try { if (osName.contains("mac")) { // 1. 0~100의 값을 0~16 단계로 변환 // (level / 100.0 * 16)을 반올림하여 목표 클릭 횟수 결정 // val targetStep = Math.round(level / 100.0 * 16).toInt() // // // 2. AppleScript 구성 // // 145(F1)는 어둡게, 144(F2)는 밝게 // val script = """ // tell application "System Events" // -- 일단 확실하게 최저(0)로 만듦 (16회 반복) // repeat 16 times // key code 145 // end repeat // // -- 그 다음 목표 단계만큼 밝게 함 // repeat $targetStep times // key code 144 // end repeat // end tell // """.trimIndent() val keyCode = if (level < 50) 145 else 144 // 145: 감소, 144: 증가 val action = if (level < 50) "어둡게" else "밝게" // 32번 연타하면 어떤 상태에서든 최소/최대에 도달합니다. val script = """ tell application "System Events" repeat 32 times key code $keyCode end repeat end tell """.trimIndent() println(script) try { val process = ProcessBuilder("osascript", "-e", script).start() process.waitFor() // println("🍏 Mac 밝기를 $level% (약 $targetStep/16 단계)로 설정했습니다.") } catch (e: Exception) { println("⚠️ 실행 실패: ${e.message}") } } else if (osName.contains("win")) { // Windows: PowerShell 사용 (0 ~ 100 사이 값) // val psCommand = "Get-CimInstance -Namespace root/WMI -ClassName WmiMonitorBrightnessMethods | ForEach-Object { \$.WmiSetBrightness(1, $level) }" // ProcessBuilder("powershell.exe", "-Command", psCommand).start() // 2: 모니터 끄기, -1: 모니터 켜기, 1: 저전력 모드 if (level < 50) { val psCommand = "Add-Type -TypeDefinition 'using System; using System.Runtime.InteropServices; public class Monitor { [DllImport(\"user32.dll\")] public static extern int PostMessage(IntPtr hWnd, int hMsg, int wParam, int lParam); public static void Off() { PostMessage((IntPtr)0xffff, 0x0112, 0xF170, 2); } }'; [Monitor]::Off()" ProcessBuilder("powershell.exe", "-Command", psCommand).start() println("🌙 윈도우 모니터를 절전 모드로 전환했습니다.") // // 0xF140은 화면 보호기를 실행하는 시스템 명령 상수입니다. // val psCommand = "Add-Type -TypeDefinition 'using System; using System.Runtime.InteropServices; public class Saver { [DllImport(\"user32.dll\")] public static extern int PostMessage(IntPtr hWnd, int hMsg, int wParam, int lParam); public static void Run() { PostMessage((IntPtr)0xffff, 0x0112, 0xF140, 0); } }'; [Saver]::Run()" // // ProcessBuilder("powershell.exe", "-Command", psCommand).start() // println("🖥️ 화면 보호기를 실행했습니다.") } else { } } println("🔆 밝기를 $level%로 설정했습니다.") } catch (e: Exception) { println("⚠️ 밝기 조절 실패: ${e.message}") } } /** * 모니터를 즉시 잠자기 모드로 전환 */ fun sleepDisplay() { try { setBrightness(10) println("🌙 [System] 오후 6시 30분: 모니터를 잠자기 모드로 전환합니다.") } catch (e: Exception) { println("⚠️ 모니터 잠자기 실패: ${e.message}") } } fun preventSleepWithRobot() { try { val robot = Robot() // 현재 마우스 위치 가져오기 val pointer = MouseInfo.getPointerInfo().location val x = pointer.x val y = pointer.y // 마우스를 1픽셀 옆으로 움직였다가 다시 제자리로 (OS는 활동으로 간주함) robot.mouseMove(x + 1, y + 1) robot.mouseMove(x, y) println("🖱️ 마우스 이벤트를 시뮬레이션하여 화면 잠금을 방지했습니다.") } catch (e: Exception) { println("⚠️ 이벤트 생성 실패: ${e.message}") } } /** * 마우스 움직임을 시뮬레이션하거나 디스플레이 깨우기 명령 실행 */ fun wakeDisplay() { try { setBrightness(80) println("☀️ 오전 8시: 모니터를 깨웁니다.") } catch (e: Exception) { println("⚠️ 모니터 깨우기 실패: ${e.message}") } } /** * 앱 종료 시 프로세스 함께 종료 */ fun stop() { process?.destroy() // 프로세스가 강제 종료되지 않을 경우를 대비해 0.5초 대기 후 강제 종료 if (process?.waitFor(500, TimeUnit.MILLISECONDS) == false) { process?.destroyForcibly() } println("🛑 [System] caffeinate 종료됨: 시스템 절전 설정이 정상화됩니다.") } }