1. Home
  2. 프로그래밍/Python
  3. [Python] 마우스 & 키보드 못쓰게 잠구기

[Python] 마우스 & 키보드 못쓰게 잠구기

서론

이 글은 서론이 좀 깁니다. 코드 이해를 위해 전공 지식을 조금 풀어서 작성해놨는데 관심 없으신 분들은 아래로 쭉 내려서 소스코드만 보시면 됩니다.

 

이전에 아두이노 레오나르도를 이용해서 절대좌표 마우스 이동을 시연하는 글을 작성한 적이 있습니다. 아두이노 레오나르도는 위 처럼 생긴 작은 보드인데 정확히는 컴퓨터는 아니고 일종의 프로그래밍 가능한 마우스(?) 로 C++ 로 코딩을 해서 저 보드에 마우스 이동 명령을 내리면, 레오나르도에 연결된 컴퓨터의 마우스를 조작할 수 있습니다.

 

로지텍이나 체리 키보드를 사용하신다면, 제공되는 전용 소프트웨어를 사용시 키 1개를 매크로로 등록해서 마우스가 DPI 조작이나 연타를 하게 하거나 키보드가 연타를 하게끔 만들 수 있습니다. 아두이노 레오나르도 역시 비슷하게 동작하는 셈이지요. 다만 프로그래밍을 할 수 있어서 조금 더 유용하달까요.

 

이미지 출처 : https://kwanulee.github.io/iot/docs/serial/serial.html

물론 아두이노에 코딩을 하고 그 소스 파일을 업로드 한 번 하면 더 이상 PC와 실시간 통신이 불가해집니다. 예를 들어 레오나르도에 지금 연결된 컴퓨터의 마우스를 (50,50) 으로 이동해! 라고 코딩해놓고 무한 반복 시키면 (while문) 레오나르도를 PC에 꼽아놓은 순간 레오나르도가 마우스처럼 동작해서 PC의 마우스 커서가 50,50 에 고정되게 됩니다.

 

하지만 이렇게 코딩해놓으면 아무짝에 없는 쓰레기가 되겠죠? 꼽자마자 마우스를 50,50 에 고정시키고 아무것도 못하게 만드는 보드라니.. 그래서 코딩을 좀 달리해서 위 사진처럼 시리얼 통신이란 것을 통해서 PC와 레오나르도가 실시간으로 통신하게끔 만들 수 있습니다. 레오나르도는 똑같이 무한 반복(while) 을 하는데 이번엔 PC의 신호를 기다려서, PC가 mv 50,50 과 같이 신호한다면 50,50 으로 이동하고 이러면 PC는 시리얼 통신만을 통해서 HW적으로 마우스 움직임을 구현할 수 있습니다.

 

연결된 레오나르도 기기는 마치 마우스처럼 동작하며, PC가 어떤 SW던 시리얼 통신을 통해 레오나르도에 움직임 명령을 내리면 레오나르도는 꼽힌 PC의 마우스를 조작하게 되는 것 입니다.

 

저는 Python 이라는 프로그래밍 언어를 이용해서 연결된 레오나르도에 시리얼 통신 신호를 보냈었는데요.

한 가지 문제가 있었습니다.

 

예를 들어 사용자가 마우스를 움직이고 있는 상황에서 파이썬 프로그램을 통해 mv 50,50 과 같은 명령을 내린 경우 50, 50 으로 레오나르도가 이동을 수행하지만 그와 동시에 사용자 마우스가 움직이면 50,50 좌표에 정확히 도달하지 못하거나 50,50에 도달하자마자 좌표가 튀어버리는 문제가 있었습니다.

 

따라서 Python 을 통해서 레오나르도에 시리얼 통신을 보내는 동안엔 사용자의 마우스 움직임을 정지시킬 필요가 있었습니다. 레오나르도는 마우스 뿐만이 아니라 키보드 처럼도 동작할 수 있는데 만약에 레오나르도가 키보드 입력을 PC에 보내는 동안엔 사용자 키보드 입력을 차단해야 했습니다.

 

그래서 파이썬으로 컴퓨터 사용자의 키보드, 마우스를 차단하는 코드를 인터넷에 찾아봤습니다. 그런데 정보가 많이 안나오더라구요. 오늘은 파이썬으로 키보드, 마우스를 잠그는 방법을 알아볼 겁니다.

 

파이썬으로 사용자의 키보드, 마우스 움직임 차단시키기 - 이론

우선 파이썬으로 키보드 / 마우스 잠금에 대한 자료가 많이 없는 것은 그만한 이유가 있습니다. 이러한 코드들은 파이썬 같은 언어보다는 C나 C++ 같은 언어의 코드로 더 잘 구할 수 있을겁니다. 이 이유를 알기 위해선 조금 거창하게 말하면 컴퓨터의 계층도를 좀 알아야 하는데요. 별 관심은 없으실 수 있겠지만 소스코드만 띡 올려놓고 가기엔 글이 너무 재미가 없기 때문에 작성하도록 하겠습니다. 별로 안궁금하고 소스코드만 궁금하신 분들은 맨 아랫 부분 소스코드만 가져가셔도 됩니다.

 

이 사진을 도대체 몇 번씩 우려먹는지 모르겠는데 컴퓨터의 계층도를 간략하게 4단계로 나타낸 사진입니다. (HW, SW 모두 포함) 자세하게 알고싶으신 분들은 해당 글을 보시구요. 보기 편하라고 특별히 이미지를 번역까지 해왔습니다. 영어의 거북함 0%! 여러가지가 보이는데 아래에 2가지만 보시면 됩니다. 컴퓨터 하드웨어와 운영체제.

 

컴퓨터 하드웨어는 말그대로 하드웨어를 이야기 합니다. 맨 아랫단계에 존재하면서 컴퓨터의 구성 요소 중에 어떻게 보면 가장 중요하다고 할 수 있습니다. 애초에 하드웨어(컴퓨터 부품)가 있어야 본체를 킬 수 있고, 뭔가를 할 수 있기 때문입니다. 컴퓨터 하드웨어는 CPU, RAM, HDD, SSD, 키보드, 마우스 등을 이야기 합니다. 돈주고 사야하는 것들이요.

 

아마 여러분들은 직접 다나와나 행복 쇼핑에서 컴퓨터를 조립했거나, 하이마트던, 용산이던 하는 곳에서 컴퓨터를 사왔거나 하셨을겁니다. 근데 컴퓨터 본체만 있어선 아무것도 할 수 있는게 없죠? 당연하게도 여러분은 컴퓨터를 제대로 사용하기 위해 운영체제(OS) 를 까셨을겁니다. 윈도우나 리눅스 같은 운영체제를 말이죠. 운영체제가 없으면 컴퓨터를 전혀 사용할 수 없기에 컴퓨터를 사용하기 위해선 필수적이라고 볼 수 있겠습니다.

 

그래서 위 사진을 보면 아시겠지만 컴퓨터 하드웨어 위에 운영체제가 존재하게 됩니다. 하드웨어가 맨 밑 단계에서 있다면 이제 하드웨어 위에서 운영체제, 즉 윈도우 같은것들이 돌아가면서 하드웨어를 관리하게 되는 겁니다.

운영체제는 실제로 컴퓨터 하드웨어에 대한 모든 것을 관리하고 있습니다.

 

당연히 우리가 쓰는 키보드, 마우스도 마찬가지겠죠?

그러면 프로그래밍 하는 입장에서 우리의 프로그램은 위 사진에서 어디에 존재하는 걸까요? 우리는 코딩을 할 때 윈도우위에서 프로그램을 돌려 코딩을 하게 됩니다. 따라서 우리의 응용 프로그램은 운영체제(OS) 위에서 실행됩니다. 우리가 만든 프로그램이 실행될 수 있는건 모두 운영체제 덕분입니다. 더블클릭해서 exe 파일을 실행시킬 수 있는것, 파이썬 인터프리터를 실행시켜서 우리가 작성한 파이썬 스크립트를 실행하는 것 모두 운영체제가 합니다.

 

그러면 결론적으로 우리가 하고 싶은 파이썬으로 마우스, 키보드 제어는 어떻게 해야할까요?

 

 

우리가 작성한 응용 프로그램이 3단계에 있으니 저렇게 운영체제를 무시하고 하드웨어에 직접 접근해서 마우스, 키보드를 정지시키면 되겠죠? 하지만 아쉽게도 운영체제를 무시하고 바로 컴퓨터 하드웨어에 접근하는건 보안상 금지되어 있습니다. 예를 들어서 응용 프로그램이 운영체제의 관리를 무시하고 RAM을 100% 써라! CPU도 100% 쓰고 하면 컴퓨터 전체 시스템이 문제가 생기겠죠?

 

그러면 애초에 응용 프로그램은 컴퓨터의 HW 자원을 일체 사용할 수 없나요? 하면 그건 아닙니다. 사실 윈도우에서 바이러스 같은걸 실행시켜보면 실제로 램도 막 100% 씩 먹게하고 CPU도 100% 씩 먹게해서 컴퓨터를 고장내는 프로그램을 만든다던가, 키보드 마우스를 마음대로 조작해서 해킹한다던가 하는 프로그램. 얼마든지 만들 수 있습니다.

 

실제 응용프로그램이 컴퓨터 하드웨어에 접근하는 방식은 이런 방식입니다. 운영체제가 이미 HW를 총괄해서 관리하고 있으니, 예를 들어서 응용 프로그램이 a라는 글자를 모니터(HW) 에 띄우고 싶다면, 운영체제한테 이렇게 요청합니다.

 

운영체제야! 나 컴퓨터 하드웨어 (모니터) 에 글자 a좀 쓰고 싶은데 출력좀 해줘라. 라구요.

이러한 요청을 윈도우에선 윈도우32 API / 리눅스나 UNIX 계열 운영체제 (애플의 mac os 같은것) 에서는 시스템 콜 이라고 합니다. 

 

그러면 운영체제는 이 요청을 듣고 컴퓨터 하드웨어를 제어하게 되는 구조인 것입니다. 모니터 출력 뿐만이 HDD/SSD 에 파일 쓰기, 키보드 마우스 입력, 프린터 출력 등등이 다 이렇게 운영체제를 경유하여 진행됩니다.

 

그리고 윈도우 32 API나 시스템 콜은 "C언어" 로 제공되며 대부분이 C언어로 구현되어 있습니다. 한마디로 운영체제에 요청을 하기 위해서 운영체제에 이미 만들어져있는 C언어 코드를 실행시켜서 운영체제가 하드웨어를 제어할 수 있게끔 하는 것이지요. 

 

이러면 2가지 의문사항이 생길 수 있습니다.

먼저 첫 번째 의문 사항은 OS는 응용 프로그램이 컴퓨터 하드웨어에 직접 접근하는걸 막아서 컴퓨터를 안전하게 보호한다고 했는데 그래봤자 시스템 콜이나 윈도우 32api 같은 C언어 코드를 실행시켜 램을 100% 쓰게 만들거나 cpu를 100% 쓰게 만들어서 컴퓨터를 망가뜨릴 수 있지 않냐? 라고 하면 그건 어느정도 맞는 말이긴 합니다.

 

* 여기 아래부턴 제 개인적인 주관과 공부하면서 작성한 내용이기에 틀린 부분이 있을 수 있습니다.

시스템 콜 부분은 많이 공부했으나 윈도우 32 API 부분은 많이 공부하지 못했습니다. 😢

 

윈도우를 기준으로는 윈도우 32 API를 쓰면 컴퓨터 HW 대부분을 운영체제에 요청하여 제어할 수 있기에 거의 모든걸 할 수 있습니다. 그럼에도 이렇게 운영체제를 경유하여 HW에 접근하는 구조의 중요성은 제 나름대로 생각해본것이라면 바로 커널의 안전성에 있습니다.

 

윈도우, 리눅스 같은 운영체제 역시 C or C++ 이라는 프로그래밍 언어로 만들어진 "프로그램" 입니다. 단 일반적으로 우리가 만든 프로그램이라고 차이점이라고 하면 컴퓨터를 키면 24시간 항시 RAM에 상주하여 돌아가는 프로그램이라는 것이죠. 운영체제에서 코드로 작성된 가장 중요한 부분을 흔히 "커널" 이라고 하는데, 이게 망가지면 컴퓨터에 흔히 말하는 블루스크린 (커널 패닉이라고도 표현) 이 뜨게 됩니다.

 

저 위처럼 시스템 콜이나, 윈도우 32 API 같은 방식을 통해 HW에 직접 접근하는걸 막으면 응용 프로그램이 막 램을 100% 씩 쓰고 CPU를 100% 씩 쓰는건 뭐 막기 어렵겠지만 커널 자체를 망가뜨리게 하는건 쉽게 하지 못하게 합니다. 커널이 해킹 당하면 실행중인 프로그램에서 비밀번호를 빼오거나 이런걸 할 수 있기에 커널의 보안성을 유지하기 위해 저런 방식을 채택하지 않았나 싶습니다.

 

어쨌던 저런 방식을 취해야 응용 프로그램 입장에서도 구조화된 방식으로 HW에 안전하게 접근할 수 있으니깐요.

참고로 윈도우야 윈도우 32 API 에 워낙 많은 변태같은 기능을 제공해서 그렇지 IOS 같은 애플 스마트폰에 들어가는 운영체제의 경우 운영체제를 열심히 해킹해냈다고 해도 운영체제에서 제공하는 기능이 워낙 별거 없어서 할 수 있는게 많이 없다고 합니다.

 

그리고 이제 두 번째 의문사항. HW에 접근하려면 운영체제의 시스템 콜이나 윈도우 32 api C언어 함수를 호출해야 모니터에 뭔가 쓴다던가 이런 걸 할 수 있다고 했는데 "나는 그런거 모르고 그냥 C언어 printf나, Java, C++, C# 같은걸로 파일 쓰기, 모니터 출력 이런거 다 잘했는데?" 라는 질문입니다.

 

예 맞습니다. 현대 프로그래밍 언어를 활용하면 윈도우 32 api나 시스템 콜 같은걸 사용하지 않고도 충분히 코딩 가능합니다. 하지만 실제로 Java던, C, C++ 이던 기계에 가깝게 LOW-LEVEL 하게 보면 HW 접근을 위해 다 내부적으로 윈도우 32 api나 리눅스 시스템 콜을 호출해서 구현하고 있습니다.

 

예를 들어 C언어의 printf() 함수의 경우 모니터에 무언가 출력하는 함수인데, 리눅스를 기준으로 이 printf 함수가 어떻게 구현되어 있는지 코드를 따라가고 따라가다 보면 리눅스 시스템 콜 write() 라는 C언어 함수를 호출해 구현하고 있음을 알 수 있습니다.

 

결론적으로 HW 제어를 위해선 예전엔 윈도우 32 api나 리눅스 시스템 콜 그리고 OS 구조를 이해하면서 코딩을 해야 했다면 요즘은 그럴 필요가 없어졌다 이거죠. 왜냐면 파이썬을 기준으로 print() 같은 쉬운 문법 한줄로 모니터 출력이 끝나니깐요. 하지만 이것도 내부 사정을 살펴보면 파이썬이 OS에 맞춰 알아서 윈도우의 경우 윈도우 32 api를 리눅스의 경우 시스템 콜을 호출해서 구현해놨을 겁니다.

 

어쨌던 현대 프로그래밍 언어들은 점점 이렇게 아랫 단계(윈도우 32api, 리눅스 시스템 콜) 에 있는 걸 몰라도 코딩 할 수 있게 발전하고 있습니다. OS를 알아야 파일을 쓸 수 있는 시대는 이미 가버린거죠. 이렇게 프로그래밍 언어가 발전해서 어려운 부분은 빠지고 중요한 부분만 남기고 간략화 시킨 것을 좀 있어보이는 말로 "추상화 되었다", "추상화 된 프로그래밍 언어" 라고 표현합니다. 

 

추상화는 쉽게 이해해서 간략화랑 비슷한 뜻이며, 추상화가 높은 프로그래밍 언어다 = 어려운 내용이 다 빠지고 간단한 내용만 남은 프로그래밍 언어다 정도로 이해할 수 있습니다.

파이썬의 경우에도 추상화 레벨(수준)이 높은 프로그래밍 언어에 속합니다.

 

그래서 지금까지 설명한 내용이 오늘 할꺼랑 무슨 상관이냐..? 하면 일단 우리가 하고 싶은건 파이썬으로 키보드, 마우스를 LOCK, 바로 정지시키는 것입니다. 파이썬의 경우 추상화 수준이 높은 프로그래밍 언어라서 키보드, 마우스를 제어하기 위해 이미 파이썬으로 구현된 코드를 찾아봤는데 정보가 그리 많지 않았습니다.

 

정보가 많이 안나오는 이유는 앞에서도 언급했지만 파이썬이 추상화 수준이 높은 언어라 그렇습니다. 키보드 / 마우스 같은 HW를 직접 제어해야할 상황에선 파이썬은 조금 부적합한 언어가 되는 것이죠. C나 C++ 같은 기계에 가까운 언어가 아닌 인간에 가까운 언어기 때문입니다. 

 

이럴땐 어쩔 수 없이 윈도우를 기준으로 윈도우 32 api, 리눅스의 시스템 콜을 호출해서 HW 인 마우스, 키보드를 잠궈야 합니다. 그나마 다행인건 파이썬의 ctypes 모듈을 활용하면 C언어로 구현된 윈도우 32 api 코드 호출이 가능합니다. 일단 저는 리눅스를 사용하지 않으니깐 윈도우 32 api 를 이용해서 잠구는걸 보여드리고자 합니다. 당연하지만 윈도우 32 api는 리눅스의 시스템 콜과 호환되지 않아 리눅스에선 동작하지 않습니다.

 

마우스, 키보드를 잠굴려면 운영체제가 마우스, 키보드 처리하는 코드를 찾아서 없는 코드로 만들어버려야 하는데 운영체제의 코드를 덮어씌우는 것을 아마 후킹? 이라고 했던 거 같은데 리버싱 공부하면서 뒤에 나오는 파트라서 아직 안배워서 아주 정확히는 잘 모르겠습니다.

 

그리고 글 쓰면서 키보드, 마우스 잠구는 라이브러리도 이미 누가 만들어놨더라구요. 이러면 이 긴글은 왜 썼지..? 싶지만 그래도 파이썬으로 윈도우 32 api 호출하는 상황이 아예 없는것도 아니고 이번에 공부한 내용들을 정리했다 싶어 일단은 놔뒀습니다.

 

파이썬으로 사용자의 키보드, 마우스 움직임 차단시키기 - 코드

대부분 긴 글의 압박을 못 견디고 여기로 오셨을거라고 생각됩니다. 파이썬으로 키보드, 마우스를 잠궈버립시다!

여러가지 코드를 제공해드립니다.

# pip install pyuac
# pip install pypiwin32
import pyuac
import time
from ctypes import windll

# 입력 받은 시간 만큼 키보드, 마우스 잠금
def block_keyboard_and_mouse(time_sec : int):
    windll.user32.BlockInput(True)
    time.sleep(time_sec)
    windll.user32.BlockInput(False)

def main():
    # 3초 만큼 키보드, 마우스 잠구기
    block_keyboard_and_mouse(3)

# 관리자 권한으로 실행되지 않았을 경우 관리자 권한으로 스크립트 자동 실행
if not pyuac.isUserAdmin():
        print("관리자 권한으로 재실행 합니다!")
        pyuac.runAsAdmin()
else:        
    main()

가장 첫 번째로 파이썬의 ctypes를 이용해 Win32API를 호출해 키보드와 마우스를 한 꺼번에 잠구는 예제입니다.

C/C++ 로 작성된 winuser.h의 BlockInput 함수를 호출하게 됩니다. 저도 Win32API로 직접 코딩을 해본적은 한번도 없고, 요새는 할필요도 없어서 모르는 함수들이 나오면 MSDN 공식 문서에서 이런 함수가 있다 정도만 읽고 넘어갑니다.

 

관리자 권한으로 실행이 강제되어, pyuac 를 통해 관리자 권한으로 스크립트가 실행되지 않았으면 강제로 스크립트를 관리자 권한으로 실행해 키보드와 마우스를 3초간 잠굽니다.

3초 말고 다른 초로 하고 싶으시면 block_keyboard_and_mouse(3) 함수에 숫자 3 대신 원하는 값을 넣으시면 됩니다. 참고로 잠궈지는 숫자에 너무 큰 값을 넣으면 키보드 마우스가 재부팅 전까지 영원히 멈춰버릴 수 있으니 주의해야 합니다.

 

해당 코드는 당연하지만 Win32API를 호출해 윈도우에서만 작동하고 리눅스엔 작동하지 않습니다. 관리자 권한 실행을 강제하는 것 역시 별로라서 이 코드는 그렇게 추천드리지 않습니다.

마우스와 키보드를 따로 잠굴 수도 없고 한 꺼번에 LOCK 이 되버려서 별롭니다.

 

기반이 되는 코드 출처는 이곳

 

disable or lock mouse and keyboard in Python?

Is there a way of disabling or locking mouse and keyboard using python? I want to freeze the mouse and disable the keyboard.

stackoverflow.com

 

그래도 윈도우에서만 마우스, 키보드를 한꺼번에 잠구는걸 원한다면 상당히 간편한 솔루션입니다. 윈도우 제공 api들은 거의 몇 십년째 바뀌지도 않기에 아마 파이썬 2 같은 구버전에서도 호환되지 않을까 싶네요.

 

import pyWinhook as pyHook
import pythoncom
import threading
import time

stop_pumping = False

def uMad(event):
    return False

def unlock():
    global stop_pumping
    time.sleep(3)  # 3초 대기
    hm.UnhookMouse()
    hm.UnhookKeyboard()
    print("키보드와 마우스 잠금이 해제되었습니다.")
    stop_pumping = True

# 후크 매니저 설정
hm = pyHook.HookManager()
hm.MouseAll = uMad
hm.KeyAll = uMad
hm.HookMouse()
hm.HookKeyboard()

print("키보드와 마우스가 잠겼습니다. 3초 후 해제됩니다.")

# 잠금 해제를 위한 스레드 시작
unlock_thread = threading.Thread(target=unlock)
unlock_thread.start()

# 메시지 펌핑 시작 (메인 스레드에서 실행)
while not stop_pumping:
    pythoncom.PumpWaitingMessages()

# 여기서부터 추가 코드를 작성 가능
print("프로그램이 계속 실행됩니다.")

두 번째로, pyHook 이라는 파이썬의 후킹 라이브러리를 통해 구현한 예제입니다. 아마 라이브러리라서 윈도우나 리눅스 둘 다 작동할걸로 예상되는데 리눅스는 확인안해봐서 모르겠지만 윈도우는 확실히 작동합니다. 

참고로 pyHook이 파이썬 2 구버전 이후로 더 이상 업데이트가 안되서 대체제인 pyWinHook 을 사용합니다.

 

마우스와 키보드 움직이는 코드에 return False를 줘서 후킹해 움직이지 못하게 하는 원리로, 코드를 조금 수정하면 키보드와 마우스 따로 잠굴 수 있습니다. 다만 메세지 펌프? 라는 윈도우의 구조를 알아야하고 pythoncom.PumpWaitingMessages() 를 실행시킨 순간 메세지 펌프가 종료될때까지 그 아래 코드 실행이 안되서 별도의 쓰레드로 타이머를 실행시켜서 3초 있다가 잠금 해제를 시키게 되어 코드가 다소 복잡해졌습니다.

 

구조도 복잡하고 뭔가 OS 의존적인 코드라 그렇게 좋아보이진 않네요.

def unlock():
    global stop_pumping
    time.sleep(3)  # 3초 대기
    hm.UnhookMouse()
    hm.UnhookKeyboard()
    print("키보드와 마우스 잠금이 해제되었습니다.")
    stop_pumping = True

몇 초 있다가 잠금이 풀릴지는 저기 time.sleep 부분을 고치시면 됩니다.

 

기반이 되는 코드 출처는 이곳

 

# pip install pynput
import os
from pynput import keyboard, mouse

"""
마우스 / 키보드 잠금을 지원하는 InputBlocker 객체의 설계도 (Class)

사용 방법:
  키보드 잠금을 원하는 경우
  객체 생성후 lock_keyboard() / unlock_keyboard() 함수를 사용한다.

마우스 잠금을 원하는 경우
  객체 생성후 lock_mouse() / unlock_mouse() 함수를 사용한다.

마우스와 키보드를 동시에 잠금을 원하는 경우
  객체 생성후 lock_all() / unlock_all() 함수를 사용한다.

"""


class InputBlocker:
    def __init__(self) -> None:
        self.key_combination = "<ctrl>+/"
        self.hotkey = keyboard.HotKey(
            keyboard.HotKey.parse(self.key_combination), self.unlock_and_exit
        )
        self.mouse_listener = mouse.Listener(suppress=True)

        self.keyboard_listener = keyboard.Listener(
            suppress=True,
            on_press=self.for_canonical(self.hotkey.press),
            on_release=self.for_canonical(self.hotkey.release),
        )

        # 키보드 / 마우스 잠금 상태
        self.is_keyboard_locked = False
        self.is_mouse_locked = False

    def lock_all(self) -> None:
        # 만약 키보드와 마우스 중 하나라도 이미 잠긴 상태로 또 lock을 호출한 경우
        # 2번 lock이 걸리면 무시한다
        if self.is_keyboard_locked or self.is_mouse_locked:
            return

        # 키보드와 마우스 모두 잠금
        self.mouse_listener.start()
        self.keyboard_listener.start()

        self.is_keyboard_locked = True
        self.is_mouse_locked = True

    def lock_keyboard(self) -> None:
        if self.is_keyboard_locked:
            return

        # 키보드만 잠금
        self.keyboard_listener.start()
        self.is_keyboard_locked = True

    def lock_mouse(self) -> None:
        if self.is_mouse_locked:
            return

        # 안전 장치인 키보드 리스너가 필요
        custom_keyboard_listener = keyboard.Listener(
            suppress=False,  # False로 지정하면 키보드는 잠기지 않는다.
            on_press=self.for_canonical(self.hotkey.press),
            on_release=self.for_canonical(self.hotkey.release),
        )

        # 마우스 잠금
        self.mouse_listener.start()

        # 키보드 리스너도 같이 실행시켜줘야 안전 장치가 작동한다.
        custom_keyboard_listener.start()

        self.is_mouse_locked = True

    def unlock_keyboard(self):
        # 역시 2번 unlock을 허용하지 않는다.
        if not self.is_keyboard_locked:
            return

        # 키보드 잠금 해제
        self.keyboard_listener.stop()
        self.is_keyboard_locked = False

    def unlock_mouse(self):
        if not self.is_mouse_locked:
            raise Exception("마우스가 이미 잠긴 상태가 아닙니다.")

        # 마우스 잠금 해제
        self.mouse_listener.stop()
        self.is_mouse_locked = False

    def show_unblock_message(self):
        print(
            f"키보드 / 마우스 잠금을 해제하고 프로그램을 강제 종료하려면 다음 키 {self.key_combination} 를 누르세요..."
        )

    def unlock_and_exit(self) -> None:
        self.unlock_all()

        # os._exit(0) 는 모든 쓰레드를 포함하여 즉시 프로세스를 강제 종료한다.
        # 메인 쓰레드, 자식 쓰레드가 남아서 종료되지 않을 수 있는데 전부 죽인다.
        os._exit(0)

    def unlock_all(self) -> bool:
        if not self.is_keyboard_locked and not self.is_mouse_locked:
            raise Exception("키보드 / 마우스 잠금이 이미 해제되어 있습니다.")

        # 키보드와 마우스 모두 잠금 해제
        self.mouse_listener.stop()
        self.keyboard_listener.stop()
        print("키보드 / 마우스 잠금이 해제되었습니다.")

        self.is_keyboard_locked = False
        self.is_mouse_locked = False

        return False

    def for_canonical(self, f):
        return lambda k: f(self.keyboard_listener.canonical(k))


def main() -> None:
    # 키보드, 마우스 모두 무한정 잠구기
    blocker = InputBlocker()
    blocker.show_unblock_message()
    blocker.lock_all()

    while True:
        pass

    # 마우스 3초간 잠구기
    # blocker = InputBlocker()
    # blocker.show_unblock_message()
    # blocker.lock_mouse()
    # time.sleep(3)
    # blocker.unlock_mouse()

    # 키보드 3초간 잠구기
    # blocker = InputBlocker()
    # blocker.show_unblock_message()
    # blocker.lock_keyboard()
    # time.sleep(3)
    # blocker.unlock_keyboard()


if __name__ == "__main__":
    main()

마지막으로 깃허브에서 구한 코드를 제가 좀 커스텀해서 만든 코드입니다. 운영체제 함수를 직접 사용하지 않고 pynput 라는 라이브러리를 통해서 키보드 또는 마우스를 마음대로 잠굽니다. 참고로 무한정 잠군 경우 ctrl + \ 을 통해서 잠금을 강제 해제하는 잠금 장치가 들어 있습니다.

 

main 함수 부분에 예제로 제공하니 제공되는 함수들을 하나씩 호출하면서 사용해보시면 됩니다.

InputBlocker 객체를 생성한다음에, 거기서 lock_keyboard 와 unlock_keyboard 그리고 time.sleep 을 활용해서 잠구시면 되겠습니다. 

 

개인적으로 생각했을때 가장 나은 솔루션이 아닌가 싶습니다.

 

깃허브에서 구한 코드 소스를 남기고 싶은데 인터넷을 뒤져봐도 어디서 가져온지 기억이 안나가지고 원 코드 작성자님께 사과의 말씀을 드립니다.

 

그래서 원래 원하던 작업을 성공했는가?

사실 글 서문에서 소개했듯이 저는 레오나르도가 마우스를 제어할 동안, 사용자 마우스 입력을 차단하는게 목적이였는데.. 문제는 윈도우 상에서 저렇게 마우스나 키보드를 차단해버리면 레오나르도 마우스, 키보드 입력도 같은 HW 입력이라 같이 차단되어 버립니다.

 

그래서 라이브러리를 사용하는건 포기하고 후킹을 통해서 디바이스가 레오나르도인 경우엔 안잠구고, 사용자의 키보드나 마우스면 잠구는 이런 형태로 가야 할 거 같은데 귀찮아서 여기까지만 하는걸로 하겠습니다 ㅎ;

 

결론적으로 제가 원하는걸 하려면 더 저수준의 제어가 필요해보입니다.

그렇다고 해도 파이썬으로 키보드, 마우스 잠구는 법은 알았으니 만족!

 

 

 

SNS 공유하기
네이버밴드
카카오톡
페이스북
X(트위터)

최근글
인기글
이모티콘창 닫기
울음
안녕
감사
당황
피폐