
    ^Cj                       % S r SSKJr  SSKrSSKrSSKrSSKrSSKrSSKrSSK	r	SSK
r
SSKrSSKrSSKrSSKrSSKrSSKJr  SSKJrJr  SSKJr  SSKJrJr  SSKrSSKJr  SSKrSS	KJrJ r   SSK!r!SS
K"J#r#  SSK$J%r%  SSK&J'r'J(r(  SSK)J*r*  SSK+J,r,  SSK-J.r.  SSK/J0r0  SSK1J2r2J3r3J4r4J5r5  SSK6J7r7J8r8J9r9J:r:  SSK;J<r<   SSK=J>r>  SSK?J@r@  SSKAJBrB  SrCSqFS\GS'   \R                  " 5       rIS'S(S jjrJ " S S5      rK " S S5      rL " S S5      rM " S  S!5      rN " S" S#5      rOS)S$ jrPS)S% jrQ\RS&:X  a  \S" \Q" 5       5      eg! \D a  rESr>Sr@SrB\ErC SrECENSrECEff = f)*up  
yjm_win2rtc_capture.py

현재 단계:
    WebSocket JPEG preview + LAN/WAN 공유 준비 + 캡처 대상 선택 확장

지원 캡처 대상:
    1) 선택 창 HWND
    2) 디스플레이 0, 1, ...
    3) 전체 바탕화면

주의:
    아직 WebRTC가 아니다. WebRTC/aiortc로 넘어가기 전, 캡처·프레임·포트·공유 URL을 검증하는 단계다.
    )annotationsN)partial)SimpleHTTPRequestHandlerThreadingHTTPServer)Path)parse_qsurlparse)ttk)Image	ImageDraw)CaptureSourceManager)set_dpi_awareness)UpnpPortMappermake_viewer_url)RemoteInputController)RemoteActivityOverlay)make_file_transfer_handler)ClipboardSyncManager)MSG_CLIPBOARD_GETMSG_HOST_OPTION_SETMSG_HOST_OPTION_ACKmake_msg)AppSettingsSnapshotSettingsload_settingssave_settings)SnapshotRingWorker)serve)Headers)Response
int | Noneselected_hwndc                   U =(       d    Sn[        [        U5      [        U5      [        U5      -   5       H  n [        R                  " [        R                  [        R                  5       nUR                  [        R                  [        R                  S5        UR                  X445        SSS5        Us  $    [        SU SX-   S-
   35      e! , (       d  f       N*= f! [         a     M  f = f)u>   preferred_port가 사용 중이면 다음 포트를 찾는다.0.0.0.0   Nu3   사용 가능한 포트를 찾지 못했습니다: ~)
rangeintsocketAF_INETSOCK_STREAM
setsockopt
SOL_SOCKETSO_REUSEADDRbindOSError)hostpreferred_portmax_try	bind_hostportss         //mnt/data/yjm_in2rtc_mod/yjm_win2rtc_capture.pyfind_free_portr8   H   s    !	Ic.)3~+>W+MN	v~~v/A/ABaV..0C0CQG	() C K O GGWWXYgYqtuYuXvw
xx CB  		s*   3C$-AC/	C$
C!	C$$
C21C2c                  ^    \ rS rSrSrSS jrS rSS jrSS jrSS jr	S	 r
S
 rS rS rSrg)WindowOverlayV   uU   마우스가 멈춘 foreground HWND를 선택하고 빨간 테두리를 표시한다.Nc                    Xl         X l        X0l        U=(       d    S U l        U=(       d    S U l        S U l        S U l        SU l        S U l        S U l	        SU l
        SU l        g )Nc                     gNF r?       r7   <lambda>(WindowOverlay.__init__.<locals>.<lambda>]   s    r@   c                     gNTr?   r?   r@   r7   rA   rB   ^   s    4r@   Tr      )rootloggeron_hwnd_change_callbackis_locked_callbackis_window_mode_callbackoverlay	last_rectrunningthreadlast_mouse_poslast_mouse_move_timemouse_stationary_threshold)selfrF   rG   rH   rI   rJ   s         r7   __init__WindowOverlay.__init__Y   s`    	'>$"4"G'>'P<$"$%!*+'r@   c                   U R                   (       a  U R                   R                  5       (       dZ  [        R                  " U R                  SS9U l         U R                   R                  5         U R                  R                  S5        g g )NTtargetdaemonzStarted HWND monitoring thread)rN   is_alive	threadingThreadmonitor_foreground_windowstartrG   debugrR   s    r7   start_monitoringWindowOverlay.start_monitoringg   s^    {{$++"6"6"8"8#**$2P2PY]^DKKKKK>? #9r@   c                V     [        U R                  5       5      $ ! [         a     gf = fr>   )boolrI   	Exceptionr_   s    r7   
_is_lockedWindowOverlay._is_lockedm   s-    	//122 		    
((c                V     [        U R                  5       5      $ ! [         a     gf = frD   )rc   rJ   rd   r_   s    r7   _is_window_modeWindowOverlay._is_window_modes   s-    	44677 		rg   c                    US:X  a  g XR                   R                  5       :X  a  g[        R                  " U5      =(       d    SnUR	                  S5      (       a  gSU;   a  SU;   a  gg! [
         a     gf = f)Nr   T yjm_win2rtczviewer.htmlF)rF   winfo_idwin32guiGetWindowText
startswithrd   rR   hwndtitles      r7   _is_own_windowWindowOverlay._is_own_windowy   s~    19		yy))++**406BE..%-5*@   		s   A+ 5A+ A+ +
A87A8c                   U R                   (       Gax   U R                  5       (       d(  U R                  5         [        R                  " S5        MP  [
        R                  " 5       n[        R                  " 5       nU R                  (       a  U R                  U:w  a  X l        Xl        X R                  -
  U R                  :  Ga  [
        R                  " 5       n[           [        nU R                  5       (       a  U(       ay  [
        R                  " U5      (       a(  [
        R                  " U5      nU R!                  U5        OS qU R#                  S S 5        [        R                  " S5         S S S 5        GM{  U[        :w  a  U R%                  U5      (       dn  Uq[
        R                  " U5      n[
        R&                  " U5      nU R!                  U5        U R#                  X65        U R(                  R+                  SX6U5        ON[        (       aC  [
        R                  " [        5      (       d$  S qU R                  5         U R#                  S S 5        S S S 5        [        R                  " S5        U R                   (       a  GMw  g g ! , (       d  f       N:= f! [,         a<  nU R(                  R/                  SU5        [        R                  " S5         S nANhS nAff = f)N皙?皙?z!Selected HWND=%s title=%s rect=%szHWND monitoring error: %sr%   )rM   ri   hide_overlaytimesleepro   GetCursorPosrO   rP   rQ   GetForegroundWindow	hwnd_lockr"   re   IsWindowGetWindowRectcreate_overlayrH   ru   rp   rG   r^   rd   error)rR   current_poscurrent_timers   current_selectedrectrt   excs           r7   r\   'WindowOverlay.monitor_foreground_window   s   lll+++--%%'JJsO&335#yy{&&4+>+>++M0<-&1#";";;t?^?^^#779D"+8(??,,1A'001ABB'/'='=>N'O $ 3 3D 904 $ < <T4 H JJsO$ #  =09L9LT9R9R,0M#+#9#9$#?D$,$:$:4$@E //5 88E KK--.QSW`de*]83D3D]3S3S,0M --/ 88tD1 #2 

3S lll  #4  !!"=sC

1sD   ;J BJ  BJ1J <CJJ 
JJ 
K#'2KK#c           	        U R                  5       (       d  U R                  5         g Uu  p#pEXB-
  nXS-
  nUS::  d  US::  a  g  U R                  (       d  [        R                  " U R
                  5      U l        U R                  R                  S5        U R                  R                  SS5        U R                  R                  SS5        [        R                  " U R                  SSS9U l	        U R                  R                  [        R                  SS9  XR                  :w  a  U R                  R                  U SU S	U S	U 35        U R                  R                  XgS
9  U R                  R                  S5        U R                  R!                  SSUS-
  US-
  SSS9  Xl        g g ! ["         a&  nU R$                  R'                  SU5         S nAg S nAff = f)Nr   Tz-topmostz-transparentcolorwhite)bghighlightthicknessfillexpandx+)widthheightallrE   red   )outliner   z!Overlay creation/update error: %s)ri   rz   rK   tkToplevelrF   overrideredirect
attributesCanvascanvaspackBOTHrL   geometryconfigdeletecreate_rectanglerd   rG   r   )	rR   r   r   yx1y1r   r   r   s	            r7   r   WindowOverlay.create_overlay   s   ##%%bA:1	H<<!{{4995--d3''
D9''(;WE iiUVW  bggd ;~~%%%q!AaS&AB""">""5),,Q519fqjRW_`,a!% &  	HKKA3GG	Hs   E)F- -
G7GGc                    U R                   (       a*   U R                   R                  5         S U l         S U l        g g ! [         a     Nf = fN)rK   destroyrd   rL   r_   s    r7   rz   WindowOverlay.hide_overlay   sG    <<$$&  DL!DN   s   = 
A
	A
c                2    SU l         U R                  5         g r>   )rM   rz   r_   s    r7   stopWindowOverlay.stop   s    r@   )r   rI   rJ   rP   rO   rL   rG   rQ   rH   rK   rF   rM   rN   )NNreturnrc   )rs   r(   r   rc   )__name__
__module____qualname____firstlineno____doc__rS   r`   re   ri   ru   r\   r   rz   r   __static_attributes__r?   r@   r7   r:   r:   V   s5    _,@.`H:"r@   r:   c                  Z    \ rS rSrSrSS jrS rS rS rS r	S	 r
SS
 jrSS jrS rSrg)WsFrameServer   u   캡처 스레드에서 들어온 JPEG bytes를 모든 viewer.html 클라이언트에 보낸다.

viewer.html에서 들어오는 텍스트 JSON은 원격 제어 이벤트로 처리할 수 있다.
Nc                @   Xl         [        U5      U l        [        U5      U l        X0l        U=(       d    [
        R                  " [        5      U l        XPl	        S U l
        S U l        [        5       U l        SU l        [        R                   " 5       U l        X`l        g r>   )r1   r(   requested_portr5   tokenlogging	getLoggerr   rG   	auto_portlooprN   setclientsrM   rZ   Eventready_eventcontrol_handler)rR   r1   r5   r   rG   r   r   s          r7   rS   WsFrameServer.__init__   sr    	!$iI	
; 1 1( ;"	u$??,.r@   c                  ^ ^^
#    [         R                  " SS9m
T R                  R                  T
5        T R                  R                  S[        T R                  5      5        U
U4S jnU U4S jn[         R                  " U" 5       5      n[         R                  " U" 5       5      n [         R                  " XE1[         R                  S9I S h  vN u  pgU H  nUR                  5       n	U	(       d  M  U	e   XE4 H  nUR                  5         M     T R                  R                  T
5        T R                  R                  S[        T R                  5      5        g  N! [         a&  n	T R                  R                  SU	5         S n	A	NS n	A	ff = f! XE4 H  nUR                  5         M     T R                  R                  T
5        T R                  R                  S[        T R                  5      5        f = f7f)	Nr%   )maxsizezviewer connected: clients=%dc                 |   >#     TR                  5       I S h  vN n TR                  U 5      I S h  vN   M3   N N7fr   )getsend)framequeue	websockets    r7   send_frames*WsFrameServer.handler.<locals>.send_frames   s4     #iik)nnU+++ )+s   <8<:<<c            	       >#    T  S h  vN n [        U [        5      (       d  M   TR                  (       d  M3  TR                  U 5      nU(       a~  [        USS5      (       al  TR                  R                  S[        USS5      5        [        USS 5      nU(       a/   TR                  [        R                  " USS95      I S h  vN   M  M  U(       d  M  [        USS5      S	:w  d  M  TR                  R                  S
[        USS5      5        GM   GN NQ! [         a(  nTR                  R                  SU5         S nAGMA  S nAff = f
 g 7f)NokFzcontrol ok: %smessagerl   dataensure_asciizcontrol ack send failed: %signoredzcontrol blocked: %s)

isinstancestrr   getattrrG   r^   r   jsondumpsrd   )r   resultr   r   rR   r   s       r7   receive_control.WsFrameServer.handler.<locals>.receive_control  s    !* aggs++0D0D0D!11':F'&$">">))*:GFIWY<Z[&vvt<V&/nnTZZSX5Y&Z Z Z  
  GFIr$Bi$O))*?QZ\^A_`a ![#, V $ 1 12OQT U UV "+sr   EEDEEEA%E'DDD	EE+*EED
E$E EEE)return_whenzviewer disconnected: %szviewer removed: clients=%d)asyncioQueuer   addrG   infolencreate_taskwaitFIRST_EXCEPTION	exceptionrd   r^   canceldiscard)rR   r   r   r   	send_task	recv_taskdonependingtaskr   r   s   ``        @r7   handlerWsFrameServer.handler   s    a(7T\\9JK	,
	a ''6	''(9:		N"),,&#33# MD nn&3I  #. /LL  'KK93t||;LM  	>KK7==	> #. /LL  'KK93t||;LMsV   B#G<)'E  E E  5E  9A%G<E   
F*FF FF A&G99G<c                   U R                   (       d  g  [        UR                  5      n[        UR                  5      R                  SS/5      S   n[        R                  " X@R                   5      (       a  g  [        b  [        c  g Sn[        SS[        SS[        [        U5      5      4/5      U5      $ ! [         a     NJf = f)	Nr   rl   r   s   Forbidden: invalid token
i  	Forbidden)zContent-Typez
text/plainzContent-Length)r   r	   pathr   queryr   secretscompare_digestrd   r    r   r   r   )rR   
connectionrequestparsedr   bodys         r7   _check_token_process_request*WsFrameServer._check_token_process_request#  s    zz	gll+FV\\*..w=a@E%%eZZ88 9
 w,36FCPTI5WXY	
 	
  		s   A#B7 7
CCc                \  #    [         c  [        S[         35      eU R                  (       a%  [	        U R
                  U R                  5      U l        [        U R                  U R
                  U R                  S S SSU R                  S9 IS h  vN   U R                  R                  SU R
                  U R                  5        U R                  R                  5         [        R                  " 5       I S h  vN   S S S 5      IS h  vN   g  N} N N	! , IS h  vN  (       d  f       g = f7f)Nu   websockets import 실패:    )max_sizecompressionping_intervalping_timeoutprocess_requestzWebSocket server ws://%s:%s)r   RuntimeError_WEBSOCKETS_IMPORT_ERRORr   r8   r1   r   r5   r   r   rG   r   r   r   r   Futurer_   s    r7   _runWsFrameServer._run8  s     =!;<T;UVWW>>&tyy$2E2EFDILLIIII ==	
 	
 KK:DIItyyQ  ".."""	
 	
 	
 #	
 	
 	
 	
s[   BD,DD,A$D6D7D;D,DD,DD,D)DD)%D,c                    U R                   (       a  g SU l         U R                  R                  5         [        R                  " U R
                  SS9U l        U R                  R                  5         g )NTrV   )rM   r   clearrZ   r[   _thread_mainrN   r]   r_   s    r7   r]   WsFrameServer.startM  sO    << &&d.?.?Mr@   c                   [         R                  " 5       U l        [         R                  " U R                  5         U R                  R	                  U R                  5       5        SU l	        U R                  R                  5         g ! [         a&  nU R                  R                  SU5         S nANMS nAff = f! SU l	        U R                  R                  5         f = f)NzWebSocket server error: %sF)r   new_event_loopr   set_event_looprun_until_completer
  rd   rG   r   rM   r   r   rR   r   s     r7   r  WsFrameServer._thread_mainU  s    **,	tyy)	#II((5 !DL  "	  	AKK:C@@	A !DL  "s)   )B 
B7B2-B: 2B77B: :#Cc                    U R                   (       a  U R                  (       d  g U R                   R                  U R                  U5        g r   )r   rM   call_soon_threadsafe_push_message_in_loop)rR   frame_bytess     r7   
push_frameWsFrameServer.push_frame`  s,    yy		&&t'A'A;Or@   c                    U R                   (       a  U R                  (       d  g U R                   R                  U R                  [	        U5      5        g r   )r   rM   r  r  r   rR   texts     r7   	push_textWsFrameServer.push_texte  s0    yy		&&t'A'A3t9Mr@   c                8   [        U R                  5       H;  n UR                  5       (       a   UR                  5         UR                  U5        M=     g ! [        R
                   a     N,f = f! [         a    U R                  R                  U5         M  f = fr   )	listr   full
get_nowaitr   
QueueEmpty
put_nowaitrd   r   )rR   r   r   s      r7   r  #WsFrameServer._push_message_in_loopj  s    $,,'E,::<<((*   ) (
 #--   ,$$U+,s4   A0A A0A-*A0,A--A00%BB)r   r   r   r1   rG   r   r5   r   r   rM   rN   r   )r$   ="  rl   NTN)r  bytesr  r   )r   r   r   r   r   rS   r   r   r
  r]   r  r  r  r  r   r?   r@   r7   r   r      s8    
/*NX
*#*	#P
N

,r@   r   c                  *    \ rS rSrSS jrS rS rSrg)StaticViewerServeriw  Nc                "   [        [        U5      R                  5       5      U l        X l        [        U5      U l        [        U5      U l        U=(       d    [        R                  " [        5      U l        XPl        X`l        S U l        S U l        g r   )r   r   resolve	directoryr1   r(   r   r5   r   r   r   rG   r   handler_factoryhttpdrN   )rR   r/  r1   r5   rG   r   r0  s          r7   rS   StaticViewerServer.__init__x  sg    T)_4467	!$iI	; 1 1( ;".
r@   c                ^   U R                   (       a  g U R                  (       a%  [        U R                  U R                  5      U l        U R                  (       a  U R                  U R                  5      nO[        [        U R                  S9n[        U R                  U R
                  4U5      U l         [        R                  " U R                   R                  SS9U l        U R                  R                  5         U R                   R#                  SU R                  U R
                  5        g )N)r/  TrV   z$HTTP viewer http://%s:%s/viewer.html)r1  r   r8   r1   r   r5   r0  r/  r   r   r   rZ   r[   serve_foreverrN   r]   rG   r   )rR   r   s     r7   r]   StaticViewerServer.start  s    ::>>&tyy$2E2EFDI**4>>:G6$..QG($))TYY)?I
&&djj.F.FtT?DIIVr@   c                    U R                   (       a<  U R                   R                  5         U R                   R                  5         S U l         g g r   )r1  shutdownserver_closer_   s    r7   r   StaticViewerServer.stop  s5    ::JJ!JJ##%DJ r@   )	r   r/  r0  r1   r1  rG   r5   r   rN   )r$   @  NTN)r   r   r   r   rS   r]   r   r   r?   r@   r7   r,  r,  w  s    	Wr@   r,  c                  :    \ rS rSrS	S jrS
S jrS rS rS rSr	g)CaptureWorkeri  c                    Xl         X l        X0l        [        U5      U l        [        U5      U l        [        U5      U l        [        U5      U l        SU l	        S U l
        SU l        SU l        g )NFr   rl   )frame_serversource_managerrG   r(   fpsjpeg_quality	max_widthrc   draw_borderrM   rN   frame_countlast_target_title)rR   r>  r?  rG   r@  rA  rB  rC  s           r7   rS   CaptureWorker.__init__  sa    (,s8-Y,!#r@   Nc           	         Ub  [        S[        U5      5      U l        Ub$  [        S[        S[        U5      5      5      U l        Ub  [        S[        U5      5      U l        Ub  [        U5      U l        g g )Nr%   _   r   )maxr(   r@  minrA  rB  rc   rC  )rR   r@  rA  rB  rC  s        r7   update_optionsCaptureWorker.update_options  sj    ?1c#h'DH# #As2s</@'A BD  C	N3DN"#K0D #r@   c                v   U R                   (       a  U R                  R                  S5        g SU l         [        R                  " U R
                  SS9U l        U R                  R                  5         U R                  R                  SU R                  U R                  U R                  U R                  5        g )Nzcapture already runningTrV   z9capture started: fps=%s quality=%s max_width=%s border=%s)rM   rG   r   rZ   r[   r
  rN   r]   r@  rA  rB  rC  r_   s    r7   r]   CaptureWorker.start  s    <<KK67&&diiEGHHNN	
r@   c                    SU l         g r>   )rM   r_   s    r7   r   CaptureWorker.stop  s	    r@   c           	     ,   [         R                   " 5        nU R                  (       Ga  [        R                  " 5       n U R                  R                  5       nU(       d  [        R                  " S5        Ma  UR                  nUR                  U5      n[        R                  " SUR                  UR                  SS5      nU R                  (       aU  [        R                  " U5      nUR                  u  p[!        S5       H"  n
UR#                  XUS-
  U
-
  U	S-
  U
-
  4SS9  M$     U R$                  (       a  UR&                  U R$                  :  ao  [)        UR*                  U R$                  UR&                  -  -  5      nUR-                  U R$                  [/        SU5      4[        R0                  R2                  5      n[4        R6                  " 5       nUR9                  US	U R:                  S
S9  U R<                  R?                  URA                  5       5        U =RB                  S-  sl!        URD                  U l#        S[/        S[)        U RN                  5      5      -  n[        R                  " 5       U-
  n[        R                  " [/        SX-
  5      5        U R                  (       a  GM  S S S 5        g ! [H         a<  nU RJ                  RM                  SU5        [        R                  " S5         S nANS nAff = f! , (       d  f       g = f)Nrx   RGBrawBGRXr   r%   )   r   r   )r   JPEGF)formatqualityoptimizezcapture error: %s      ?g      ?gMbP?)(mssrM   r{   perf_counterr?  
get_targetr|   monitor_dictgrabr   	frombytessizebgrarC  r   Drawr'   	rectanglerB  r   r(   r   resizerI  
ResamplingBILINEARioBytesIOsaverA  r>  r  getvaluerD  rt   rE  rd   rG   r   r@  )rR   sctstartedrW   monitorshotpildrawwhi
new_heightbufr   intervalelapseds                   r7   r
  CaptureWorker._run  s1   WWY#,,,++-$!00;;=F!

3 $11G88G,D//%DIIufUC''(~~c2"xx!&qA NNA!a%!)QUQY+GQ\N] "* ~~#))dnn*D%(t~~		7Q)R%S
!jj$..#a:L)MuO_O_OhOhi**,CHHS9J9JUZH[%%00@$$)$-3\\D*
 QDHH!66++-7

3uh&89:C ,,, Y8 ! $KK%%&93?JJsOO$9 YsB   (L7J<6L8GJ<A-L<
L2K=8L=LL
L)rC  r@  rD  r>  rA  rE  rG   rB  rM   r?  rN   )   F      T)NNNN)
r   r   r   r   rS   rK  r]   r   r
  r   r?   r@   r7   r<  r<    s    $1
#;r@   r<  c                  D   \ rS rSrS r\S*S j5       rS+S jrS,S jrS,S jr	S,S jr
S-S jrS.S	 jrS/S
 jr\S0S j5       rS rS rS rS1S jrS rS rS rS2S jrS rS rS rS rS rS rS rS rS rS r S r!S  r"S3S! jr#S" r$S# r%S$ r&S4S% jr'S.S& jr(S' r)S( r*S)r+g)5
Win2RtcAppi  c           
     T  ^  [        5         UT l        UT l        [        R                  " S5      T l        T R
                  R                  UR                  (       a  [        R                  O[        R                  5        [        R                  " 5       nUR                  [        R                  " S5      5        T R
                  R                  U5        T R                  R                  S5        T R                  R                  S5        UR                   (       a  ["        R$                  " S5      OST l        ['        T R
                  5      T l        [&        R*                  " 5       T l        [/        [0        5      R3                  5       R4                  nUT l        US-  T l        [;        T R8                  5      T l        US-  T l        [A        T RB                  5      T l"        [F        RH                  " S	S
9T l%        [M        T R                  SS9T l'        [F        RH                  " S	S
9T l(        [F        RH                  " S	S
9T l)        US-  T l*        [W        T RD                  U 4S jT R
                  T RN                  RX                  S9T l-        []        T R                  U 4S jT R^                  T RN                  RX                  T R
                  SSS9T l0        T R`                  Rc                  5         [e        URf                  URh                  T R                   T R
                  URj                  (       + T Rl                  S9T l7        U 4S jn[q        T R>                  URf                  URr                  T R
                  URj                  (       + US9T l:        [w        T Rn                  T RD                  T R
                  URx                  URz                  UR|                  UR~                  S9T l@        [        T RD                  T R
                  U 4S jS9T lB        [F        R                  " SS
9T lD        [F        R                  " SS
9T lE        [F        R                  " SS
9T lG        [F        RH                  " S	S
9T lH        [F        RH                  " UR~                  S
9T lI        [F        R                  " URx                  S
9T lJ        [F        R                  " URz                  S
9T lK        [F        R                  " UR|                  S
9T lL        [F        R                  " SS
9T lM        [F        R                  " SS
9T lN        [F        R                  " SS
9T lO        T R<                  R                  n[F        RH                  " [        UR                  5      S
9T lS        [F        R                  " [        UR                  5      S
9T lV        [F        R                  " [        UR                  5      S
9T lY        [F        R                  " [        UR                  5      S
9T l]        [F        R                  " [        UR                  5      S
9T l_        [F        R                  " [        UR                  5      S
9T la        [F        R                  " [        UR|                  5      S
9T lb        [F        R                  " [        UR                  5      S
9T ld        [        T R                  T R
                  T R                  U 4S  jU 4S! jS"9T lg        T R                  R                  5         T R                  5         T RD                  R                  5         T R                  R                  T R<                  R                  5        T R                  R                  S#T R                  5        T R                  5         g )$Nrm   z%(levelname)s: %(message)sz+yjm_win2rtc - Windows Remote Desktop Bridge940x850rz  rl   zyjm_win2rtc_config.jsonwebF)valuei  )hold_ms
recv_filesc                 8   > T R                   R                  5       $ r   )control_varr   r_   s   r7   rA   %Win2RtcApp.__init__.<locals>.<lambda>  s    (8(8(<(<(>r@   )is_enabled_callbackrG   status_callbackc                 8   > T R                   R                  5       $ r   )clipboard_varr   r_   s   r7   rA   r    s    (:(:(>(>(@r@   i  i@ )r  notify_viewers_callbackr  rG   poll_ms	max_chars)r   r   c           
        > [        U TR                  TR                  U4S jTR                  R                  TR
                  TR                  S9$ )Nc                 8   > T R                   R                  5       $ r   )file_transfer_varr   r_   s   r7   rA   DWin2RtcApp.__init__.<locals>._http_handler_factory.<locals>.<lambda>$  s    D,B,B,F,F,Hr@   )web_dirrecv_dirr   r  r  web_notify_callbackrG   )r   r  r   remote_activity_overlayshownotify_viewersrG   )r/  rR   s    r7   _http_handler_factory2Win2RtcApp.__init__.<locals>._http_handler_factory  sC    -!jj$H $ < < A A$($7$7{{ r@   )r   r0  r@  rA  rB  rC  c                n   >^  [        TS5      (       a!  TR                  R                  SU U4S j5      $ S $ )N
status_varr   c                 :   > TR                   R                  T 5      $ r   )r  r   msgrR   s   r7   rA   7Win2RtcApp.__init__.<locals>.<lambda>.<locals>.<lambda>?  s    4??CVCVWZC[r@   )hasattrrF   afterr  s   `r7   rA   r  ?  s7    `ghlnz`{`{		;[(\  )F  BF  )Fr@   )r     선택된 창: 없음desktopr   u
   대기 중u   서버 시작 전u   UPnP 실행 전c                 8   > T R                   R                  5       $ r   )lock_varr   r_   s   r7   rA   r  \  s    t}}'8'8':r@   c                 >   > T R                   R                  5       S:H  $ )Nwindow)
source_varr   r_   s   r7   rA   r  ]  s    DOO,?,?,AX,Mr@   )rI   rJ   WM_DELETE_WINDOW)or   rF   argsr   r   rG   setLevelverboseDEBUGINFOStreamHandlersetFormatter	Formatter
addHandlerrt   r   r   r   token_urlsafer   upnp
get_lan_iplan_ipr   __file__r.  parentbase_dirconfig_pathr   app_settings
viewer_dirr   get_selected_hwndr?  r   
BooleanVarr  r   r  r  r  r  r   r  remote_controlr   r  clipboard_syncr]   r   r1   ws_portno_auto_porthandle_ws_text_message	ws_serverr,  	http_porthttp_serverr<  r@  rX  rB  bordercapture_workerr   snapshot_ring	StringVarhwnd_varr  IntVardisplay_varr  
border_varfps_varquality_varmax_width_varr  local_url_varshare_url_varsnapshotrc   enabledai_snapshot_varr   folderai_snapshot_folder_varr(   
keep_countai_snapshot_keep_var	DoubleVarfloatinterval_secai_snapshot_interval_varmin_save_interval_secai_snapshot_min_interval_varchange_threshold_percentai_snapshot_change_varai_snapshot_max_width_varrA  ai_snapshot_quality_varr:   on_hwnd_changerK   r`   setup_uiset_desktopupdate_settingsprotocolon_closerefresh_local_url_label)rR   rF   r  r   r  r  snaps   `      r7   rS   Win2RtcApp.__init__  sF   		''6dllW]]M'')W../KLMw'		EF		9%26**W**2."
"4;;/	$//1>))+22 #&??)$*:*:;"U*243I3IJ==u5'<TYYPT'U$!#U!;]]7 </3 >;; 88==	
 3II @$($7$7 88==;;
 	!!#&IILLJJKK+++ 77
		 .OOIINNKK+++1
 ,NNKKnn
 0KK F
 +BC,,Y7991-E2--dkk:yytxx0994<<8YYT^^<,,\:\\0CD\\0AB  ))!}}43EF&(llT[[9I&J#$&IIC4H$I!(*5ARAR;S(T%,.LLuTE_E_?`,a)&(llt?\?\9]&^#)+T^^9L)M&')yys4;L;L7M'N$$IIKK:$M
 	%%''')**4+<+<+E+EF		-t}}=$$&r@   c                 P    [            [        sS S S 5        $ ! , (       d  f       g = fr   )r   r"   r?   r@   r7   r  Win2RtcApp.get_selected_hwndf  s      YYs   
%c                4    U R                   R                  S;   $ )N)r$   rl   N)r  r1   r_   s    r7   is_bound_allWin2RtcApp.is_bound_allk  s    yy~~!666r@   c                \    U R                  5       (       a  S$ U R                  R                  $ )Nz	127.0.0.1)r  r  r1   r_   s    r7    get_http_url_host_for_local_open+Win2RtcApp.get_http_url_host_for_local_openn  s#    "//11{Etyy~~Er@   c                \   U R                  5       (       a  U R                  OU R                  R                  nU R                  5       (       a  U R                  OU R                  R                  n[	        XR
                  R                  X R                  R                  U R                  5      $ r   )	r  r  r  r1   r   r  r5   r  r   )rR   	http_hostws_hosts      r7   get_lan_viewer_urlWin2RtcApp.get_lan_viewer_urlq  sp    #'#4#4#6#6DKKDIINN	!%!2!2!4!4$++$))..y*:*:*?*?..J]J]_c_i_ijjr@   c                    U R                  5       nUn[        XR                  R                  X R                  R                  U R
                  5      $ r   )r  r   r  r5   r  r   )rR   r1   r  s      r7   get_local_viewer_urlWin2RtcApp.get_local_viewer_urlv  sA    446t%5%5%:%:G^^EXEXZ^ZdZdeer@   c                     U R                   R                  [        R                  " USS95        g ! [         a&  nU R
                  R                  SU5         S nAg S nAff = f)NFr   zviewer notify failed: %s)r  r  r   r   rd   rG   r^   )rR   payloadr   s      r7   r  Win2RtcApp.notify_viewers{  sL    	?NN$$TZZe%LM 	?KK8#>>	?s   .1 
A!AA!c                r    [         R                  " U5      n[        U[        5      (       a*  UR                  S5      [        :X  a  U R                  U5      $ U R                  R                  U5      nU(       a  [        USS5      S:w  a  U$ U R                  R                  U5      $ ! [         a    Sn Nf = f)uN   viewer.html에서 들어오는 JSON 메시지를 기능별로 라우팅한다.Ntyper   rl   r   )r   loadsrd   r   dictr   r   handle_host_option_messager  handle_text_messager   r  )rR   r   r   r   s       r7   r  !Win2RtcApp.handle_ws_text_message  s    	::g&D dD!!dhhv&6:M&M22488$$88Agfi4	AM""66w??  	D	s   B' 'B65B6c                   SSK Jn  U R                  R                  5       (       d	  U" SS5      $ [	        UR                  SS5      5      nUR                  S5      nSnSnUS	:X  aj  [        U5      nU R                  R                  U5        U R                  R                  US
9  SnSU(       a  SOS 3nU R                  R                  U5        [        [        X4XVS9nU R                  U5        U" XV5      $ )Nr   )ControlResultFz,host option blocked: remote control disablednamerl   r  zunknown optionrC  )rC  Tu   빨간 테두리 ONOFF)r  r  r   r   )yjm_remote_controlr  r  r   r   rc   r  r   r  rK  r  r   r   r  )	rR   r   r  r  r  r   r   flagr  s	            r7   r  %Win2RtcApp.handle_host_option_message  s    4##%% (VWW488FB'(!"= ;DOO%..4.@B)$$E)BCGOO(.T2_G$R))r@   c                   UR                  S5      (       a  SOSnUR                  S5      (       a  SUR                  S5       3OSnUR                  S5      (       a  SUR                  S5       3OSnUR                  SU S-   5      nS	U  S
U S[        US   5       S[        US   5       S[        US   5       S[        US   5       U U U 3$ )Nprimaryu    / 주모니터rl   source / deviceindexr%      디스플레이 z / mss[z] - r   r   r   z @ left,top)r   r(   )idxmr  r  r  
real_indexs         r7   format_display_labelWin2RtcApp.format_display_label  s    '(uuY'7'7#R,-EE(OO3quuX'(,-EE(OO3quuX'(UU7C!G,
se7:,d3qz?:K1SQRS[Q\M]L^ _QvY #ah-	&&K	
r@   c           	     j   U R                   R                  5       n[        U5       VVs/ s H  u  p#U R                  X#5      PM     nnnU(       d  S/nX@R                  S'   U R
                  R                  5       nU(       a  [        [        SU5      [        U5      S-
  5      nU R
                  R                  U5        U R                  R                  U5        U R                   R                  U5        U R                  R                  S[        U5       S35        [        U5       H0  u  p#U R                  R                  SX R                  X#5      5        M2     g U R
                  R                  S5        U R                  R                  S5        U R                  R                  S5        g s  snnf )	Nu   디스플레이 없음valuesr   r%   r  u   개 감지됨zdisplay %s: %su/   디스플레이를 감지하지 못했습니다)r?  refresh_displays	enumerater  display_combor  r   rJ  rI  r   r   currentset_displayr  rG   r   )rR   displaysr  r  display_namesr&  s         r7   refresh_display_combo Win2RtcApp.refresh_display_combo  s[   &&779IRS[I\]I\vs223:I\]56M'48$""&&(#a/3x=1+<=G  )&&w/++G4OO"23x=/ OP#H-  !138Q8QRU8YZ .   #&&q)OO QR# ^s   F/c                    [        [        5      R                  5       R                  S-  n[        R
                  " [        R                  [        U5      /[        UR                  5      S9  U R                  R                  S5        g! [         aD  nU R                  R                  SU 35        U R                  R                  SU5         SnAgSnAff = f)u/   별도 모니터 진단 도구를 실행한다.zyjm_monitor_lab.py)cwdu-   모니터 진단 도구를 실행했습니다u    모니터 진단 실행 실패: zmonitor lab open error: %sN)r   r  r.  r  
subprocessPopensys
executabler   r  r   rd   rG   r   )rR   scriptr   s      r7   open_monitor_labWin2RtcApp.open_monitor_lab  s    	A(^++-447KKFcnnc&k:FMM@RSOO OP 	AOO"B3% HIKK:C@@	As   BB	 	
C:CCc           	        [         R                  " U R                  SS9nUR                  [        R
                  SS9  [         R                  " USSS9R                  SS	9  [         R                  " US
S9nUR                  [        R                  SS9  [         R                  " USSU R                  U R                  S9R                  SSSSSS9  [         R                  " USSS9U l        U R                  R                  SU R                   5        [         R                  " USSU R                  U R                  S9R                  SSSSSS9  U R                  R                  SSSSSS9  [         R                  " U5      nUR                  SSSSSS9  [         R"                  " USU R$                  S9R                  [        R&                  S S!9  [         R"                  " US"U R(                  S9R                  [        R&                  S#9  [         R                  " U5      nUR                  SSS$SSS%S&9  [         R                  " US'S(U R                  U R                  S9R                  [        R&                  S#9  UR+                  SSS)9  U R%                  5         [         R                  " XR,                  S*9R                  SS S+9  [         R.                  " US,U R0                  U R2                  S-9R                  SS.S+9  [         R.                  " US/U R4                  U R6                  S-9R                  SS.S+9  [         R                  " U5      nUR                  [        R                  S.S9  [         R.                  " US0U R8                  U R:                  S-9R                  [        R&                  S#9  [         R"                  " US1U R<                  S9R                  [        R&                  S2S!9  [         R                  " U5      nUR                  [        R                  S3S9  [         R.                  " US4U R>                  U R@                  S-9R                  [        R&                  S#9  [         R"                  " US5U RB                  S9R                  [        R&                  S2S!9  [         R                  " US6S9nUR                  [        R                  S3S9  [         R                  " US7S9R                  SSSSS8S9  [         RD                  " USS9U RF                  S8S:9R                  SSSS8S8S9  [         R                  " US;S9R                  SSSSS8S9  [         RD                  " US<S=U RH                  S8S:9R                  SS$SS8S8S9  [         R                  " US>S9R                  SS?SSS8S9  [         RD                  " USS@SAU RJ                  SSB9R                  SSSS8S8S9  [         R.                  " USCU RL                  SD9R                  SS8SSES8S9  [         R"                  " USFU RN                  S9R                  SSGSSS8S9  [         R                  " USHS9nUR                  [        R                  S3S9  [         R.                  " USIU RP                  SD9R                  SSSSSS9  [         R                  " USJS9R                  SSSKS?SS9  [         RR                  " XRT                  SLSM9R                  SSS$SS?SS&9  [         R"                  " USNU RV                  S9R                  SSSS8SS9  [         R                  " USOS9R                  SSSKS?SS9  [         RD                  " USSPU RX                  S8S:9R                  SSSS?SS9  [         R                  " USQS9R                  SSSKS?SS9  [         RD                  " USRSSSTU RZ                  SGSB9R                  SS$SS?SS9  [         R                  " USUS9R                  SS?SKS?SS9  [         RD                  " USTSVSTU R\                  SGSB9R                  SSSS?SS9  [         R                  " USWS9R                  SSSKS?SS9  [         RD                  " USXSYSTU R^                  SGSB9R                  SSSS?SS9  [         R                  " US>S9R                  SSSKS?SS9  [         RD                  " USS@SAU R`                  SSB9R                  SS$SS?SS9  [         R                  " USZS9R                  SS?SKS?SS9  [         RD                  " US<S=U Rb                  SGS:9R                  SSSS?SS9  [         R"                  " US[U Rd                  S9R                  S$SSSSS\S&9  [         R                  " US]S^S_S`9R                  S$SS?SS?S\S&9  UR+                  SSS)9  [         R                  " U5      n	U	R                  [        R                  S3S9  [         R"                  " U	SaU Rf                  S9R                  [        R&                  S S!9  [         R"                  " U	SbU Rh                  S9R                  [        R&                  S S!9  [         R"                  " U	ScU Rj                  S9R                  [        R&                  S S!9  [         R"                  " U	SdU Rl                  S9R                  [        R&                  S S!9  [         R                  " USeS9n
U
R                  [        R                  S3S9  [         R                  " U
SfS9R                  SSSgSSS9  [         R                  " XRn                  S^ShSi9R                  SSSSSS9  [         R                  " U
SjS9R                  SSSgSSS9  [         R                  " XRp                  S^ShSi9R                  SSSSSS9  U
R+                  SSS)9  [         R                  " U5      nUR                  [        R                  S3S9  [         R"                  " USkU Rr                  S9R                  [        R&                  S S!9  [         R"                  " USlU Rt                  S9R                  [        R&                  S S!9  [         R                  " XRv                  S*9R                  SSmS+9  [         R                  " USnSoSpS`9R                  SS2S+9  g )qNrz  )paddingTr   u   원격 화면 대상)zSegoe UI
   bold)r  fontrr  )anchorzRemote Desktop Source)r  )r   r7  )r   padyu   1. 전체 바탕화면r  )r  r  variablecommandr         )rowcolumnstickypadxr;  readonlyB   )stater   z<<ComboboxSelected>>u   2. 디스플레이displayr%   werE   u   디스플레이 새로고침)r  r=  )r      )siderC  u   모니터 진단)rJ     )rE   rI  )r@  rA  
columnspanrB  rC  r;  uP   고급/보기용: 선택 창(HWND) 캡처 - 원격제어 메인 용도는 아님r  )weight)textvariable)r:  r;  ul   선택 창 고정(고급 창 캡처용) - 원격 데스크톱 사용 중에는 전체/디스플레이 권장)r  r<  r=  )r   r   uc   원격 제어 허용 - viewer에서 클릭/우클릭/더블클릭/휠을 실제 PC 좌표로 실행uO   파일전송 허용 - viewer에서 보낸 파일을 recv_files 폴더에 저장u   수신 폴더 열기)r7  r   )r   r7  u;   클립보드 수동 전송 허용 - 자동 동기화 없음u)   현재 host 클립보드 viewer로 전송u   전송 옵션FPSrI     )from_torN  r   u   JPEG 품질r  rH  rB  r   i      )rQ  rR  	incrementrN  r   u   원격 화면 빨간 테두리)r  r<  r7  u   옵션 적용   u,   AI 스냅샷 / Google Drive 동기화 폴더u"   변화 있을 때 스냅샷 저장u   폴더e&   )rN  r   u   폴더 열기u   최근개수   u	   확인초rx   g      N@rZ  u   저장간격초g     r@u
   변화율%ry   g      Y@u   품질u   스냅샷 설정 저장+적용)rE   r>  u   Google Drive for Desktop 동기화 폴더를 지정하면 Drive 앱이 업로드를 담당합니다. API 업로드는 별도 단계입니다.z#555555i  )r  
foreground
wraplengthu   서버+캡처 시작u   캡처 중지u   로컬 viewer 열기u   LAN URL 복사u   주소zLAN:nwi  )rN  rY  rZ  u   공유:u   UPnP 열고 공유 URL 복사u   UPnP 닫기)r   r   u   주의: UPnP 공유는 외부 노출입니다. 테스트가 끝나면 원격 제어 OFF + 파일전송 OFF + 클립보드 OFF + UPnP 닫기를 누르세요.z#aa0000i  )<r
   FramerF   r   r   r   Label
LabelFrameXRadiobuttonr  on_source_changedgridComboboxr%  r/   on_display_selectedButtonr*  LEFTr3  columnconfigurer  Checkbuttonr  on_lock_changedr  on_control_changedr  on_file_transfer_changedopen_recv_dirr  on_clipboard_changedsend_host_clipboard_nowSpinboxr  r  r  r  apply_capture_optionsr  Entryr  open_ai_snapshot_folderr  r  r  r  r  r  save_apply_ai_snapshot_settingsstart_previewstop_captureopen_local_viewercopy_lan_urlr  r  start_upnp_and_copy_share_url
close_upnpr  )rR   r   src_box	diag_btnsadvancedfile_rowclip_rowopt_boxai_boxbtnsurl_box	upnp_btnss               r7   r  Win2RtcApp.setup_ui  s$   		$))R0


-		%4;STYYadYe..-DE"$$W-&>iZ^ZiZisw  tJ  tJ  	K  	P  	P  UV  _`  il  st  {|  	P  	} \\'2N 68P8PQ&:)VZVeVeos  pF  pF  	G  	L  	L  QR  [\  eh  op  wx  	L  	yAa11MIIg&	1QsC

9#A4KeKefkkqsqxqx  @Fk  	G

9#5t?T?TUZZ`b`g`gZh99W%!A!DqvVc__**	
 $BGG$
!,""$		%mm49969R]]((		

 $c$
'v%%++		

 $c$
'99U#244f-b++11		

 $BGG$


8"8$BTBTUZZ`b`g`gnuZv99U#244g.N''--		

 $BGG$


8"MW[WsWstyy  @B  @G  @G  NUy  	V.._="$$W-		'&++!CaVW+XG1$,,aPUUZ[denqxy  ABU  	C		'.33!CVW^_3`G2"4;K;KSTUZZ_`ijsv}~  FGZ  	H		',11a#TU\]1^G14K]K]efgllqr{|  FI  PQ  XYl  	Z&FQUQ`Q`affkluv  @C  JL  STf  	U

7$:T:TUZZ_`ijsv}~  FGZ  	H,Z[G,%ITXThThinnst}~  HK  RS  Z[n  	\		&x(--!AcPQXY-Z		&'B'B"MRRWXabopy}  EF  MNR  	O

69U9UV[[`ajktw~  GH[  	I		&~.33!CVW^_3`F!$:S:S[\]bbghqr{~  FG  NOb  	P		&{+00QqST[\0]F#$#DLiLiqrsxx}~  HI  RU  \]  dex  	f		&01661QsYZab6cF#%3TMnMnvwx}}  CD  MN  WZ  ab  ij}  	k		&|,11a#TU\]1^F#%3TMhMhpqrww|}  GH  QT  [\  cdw  	e		&{+00QqST[\0]F!$JhJhpqrww|}  GH  QT  [\  cdw  	e		&x(--!AcPQXY-ZF"$:V:V^_`eejktu  B  IJ  QRe  	S

6 @$JnJnottyz  DE  RS  \_  fg  ntt  	u		&   o  |E  RU  	V  	[  	[  `a  jk  xy  BE  LM  TZ  	[  	[q+yy		rtt'	*

44d>P>PQVV\^\c\cjpVq

4ot7H7HINNTVT[T[bhNi

44d>T>TUZZ`b`g`gntZu

4.8I8IJOOUWU\U\ciOj..X6"$$W-		'',,1TPQXY,Z		'(:(:y]`affkluv  @C  JK  RSf  	T		'	*//AaST[\/]		'(:(:y]`affkluv  @C  JK  RSf  	T!,IIe$	BDDw/

9#BDLnLnottz|  {B  {B  IOt  	P

9=$//JOOUWU\U\ciOj		%oo6;;3V;T		 p 		

 $c$
(r@   c                   [        [        U R                  R                  5       5      [	        U R
                  R                  5       =(       d    S5      [        S[        U R                  R                  5       5      5      [        S[        U R                  R                  5       5      5      [        S[        U R                  R                  5       5      5      [        S[        U R                  R                  5       5      5      [        S[        U R                  R                  5       5      5      [        S[        S[        U R                  R                  5       5      5      5      S	9$ )
Nsnapshots_driver%   rx   rZ  ry   r   r  rH  )r  r  r  r  r  r  rB  rA  )r   rc   r  r   r   r  rI  r(   r  r  r  r  r  r  rJ  r  r_   s    r7   build_ai_snapshot_settings%Win2RtcApp.build_ai_snapshot_settingsN  s    --1134t22668M<MN1c$";";"?"?"ABCS%(E(E(I(I(K"LM"%c51R1R1V1V1X+Y"Z%(eD4O4O4S4S4U.V%W!S!?!?!C!C!EFGRRT-I-I-M-M-O)P!QR	
 		
r@   c           	     R   U R                  5       nXR                  l        [        U R                  U R                  5        U R
                  R                  U5        U R                  R                  SUR                  (       a  SOS SUR                   SUR                   S35        g )Nu#   AI 스냅샷 설정 저장+적용: r  r  r  u
    / 최근 u   개)r  r  r  r   r  r  r  r  r   r  r  r  )rR   sts     r7   rs  *Win2RtcApp.save_apply_ai_snapshot_settingsZ  s    ,,.%'"d&&(9(9:**2.A"**$Z_A``cdfdmdmcnnxy{  zG  zG  yH  HK  L  	Mr@   c                   U R                  5       n[        UR                  5      R                  5       nUR	                  5       (       d  [        R
                  " 5       U-  n UR                  SSS9  [        R                  " [        U5      5        g ! [         a(  nU R                  R                  SU 35         S nAg S nAff = f)NTparentsexist_oku#   AI 스냅샷 폴더 열기 실패: )r  r   r  
expanduseris_absoluter-  mkdiros	startfiler   rd   r  r   )rR   r  r  r   s       r7   rr  "Win2RtcApp.open_ai_snapshot_foldera  s    ,,.bii++-!!##XXZ&(F	MLLL5LLV% 	MOO"EcU KLL	Ms   "/B 
CB??Cc                   U R                   R                  5       nUS:X  a6  U R                  R                  5         U R                  R                  S5        g US:X  aP  U R                  R                  5         U R                  R                  5         U R                  R                  S5        g US:X  aF  U R                  5         U R                  R                  5         U R                  R                  S5        g g )Nr  uJ   고급 선택 창 캡처 모드: 창 위에 마우스를 2초 멈추세요r  u   전체 바탕화면 모드rG  u   디스플레이 모드)
r  r   r?  
set_windowr  r   r  rK   rz   rd  )rR   modes     r7   ra  Win2RtcApp.on_source_changedl  s    ""$8**,OO lmY++-LL%%'OO <=Y$$&LL%%'OO 89 r@   Nc                ,   [        SU R                  R                  5       5      nU R                  R	                  U5        U R
                  R                  U5        U R                  R	                  S5        U R                  R	                  SU S35        g )Nr   rG  r  u    선택)	rI  r%  r&  r  r   r?  r'  r  r  )rR   eventr  s      r7   rd  Win2RtcApp.on_display_selectedz  ss    !T''//12S!'',I&.se7;<r@   c                L   U R                  5       nU R                  R                  5       (       aZ  U(       a7  [        R                  " U5      (       a  U R
                  R                  S5        g U R
                  R                  S5        g U R
                  R                  S5        g )NuA   선택 창 고정됨. 다른 창으로 자동 변경되지 않음u%   고정할 선택 창이 아직 없음u   선택 창 고정 해제됨)r  r  r   ro   r   r  r   )rR   rs   s     r7   ri  Win2RtcApp.on_lock_changed  so    %%'==))$//##$gh##$KLOO =>r@   c                    U R                   R                  5       (       a  U R                  R                  S5        g U R                  R                  S5        g )Nun   원격 제어 허용됨. 공유 URL 사용자는 viewer에서 제어 ON 후 실제 클릭할 수 있습니다.u   원격 제어 차단됨)r  r   r  r   r_   s    r7   rj  Win2RtcApp.on_control_changed  sC    !!OO  !Q  ROO 9:r@   c                V   U R                   R                  5       (       aY  U R                  R                  SSS9  U R                  R                  SU R                   35        U R                  SSSSS.5        g U R                  R                  S	5        U R                  SSS
SS.5        g )NTr  u'   파일전송 허용됨. 수신 폴더: notifyfiler  u   파일전송 허용)r  kindrF  filenameu   파일전송 차단됨disabledu   파일전송 차단)r  r   r  r  r  r   r  r_   s    r7   rk  #Win2RtcApp.on_file_transfer_changed  s    !!%%''MMt<OO"I$-- YZ6Icx yzOO 896Jdy z{r@   c                    [        U R                  R                  5       5      nU R                  R	                  U5        U(       a  U R
                  R                  S5        g U R
                  R                  S5        g )NuN   클립보드 수동 전송 허용됨. 자동 동기화는 하지 않습니다.u$   클립보드 수동 전송 차단됨)rc   r  r   r  notify_stater  r   )rR   r  s     r7   rm  Win2RtcApp.on_clipboard_changed  sX    t))--/0((1OO pqOO FGr@   c                &   U R                   R                  5       (       d  U R                  R                  S5        g U R                  R                  [        R                  " [        [        5      SS95        U R                  R                  S5        g )Nu;   먼저 클립보드 수동 전송 허용을 체크하세요Fr   u?   현재 host 클립보드를 viewer로 전송 요청했습니다)
r  r   r  r   r  r  r   r   r   r   r_   s    r7   rn  "Win2RtcApp.send_host_clipboard_now  sf    !!%%''OO ]^//

8DU;Vej0kl]^r@   c                     U R                   R                  SSS9  [        R                  " [	        U R                   5      5        g ! [
         a(  nU R                  R                  SU 35         S nAg S nAff = f)NTr  u   수신 폴더 열기 실패: )r  r  r  r  r   rd   r  r   r  s     r7   rl  Win2RtcApp.open_recv_dir  sc    	GMMt<LLT]]+, 	GOO"?u EFF	Gs   AA 
A8A33A8c                    U(       a@  [         R                  " U5      (       a%  U R                  R                  SU SUS S  35        g U R                  R                  S5        g )Nu   선택된 창: r  P   r  )ro   r   r  r   rr   s      r7   r  Win2RtcApp.on_hwnd_change  sM    H%%d++MMvSsEFMM56r@   c           
        U R                   R                  U R                  R                  5       U R                  R                  5       U R
                  R                  5       U R                  R                  5       S9  U R                  R                  SU R                   R                   SU R                   R                   SU R                   R                   SU R                   R                   35        g )Nr  u   옵션 적용: fps=z
, quality=z, max_width=z	, border=)r  rK  r  r   r  r  r  r  r   r@  rA  rB  rC  r_   s    r7   rp   Win2RtcApp.apply_capture_options  s    **  "))--/((,,.++-	 	+ 	
 	!$"5"5"9"9!:*TEXEXEeEeDffrsw  tG  tG  tQ  tQ  sR  R[  \`  \o  \o  \{  \{  [|  }	
r@   c                V    U R                   R                  U R                  5       5        g r   )r  r   r  r_   s    r7   r  "Win2RtcApp.refresh_local_url_label  s    t6689r@   c                \   U R                  5         U R                  R                  5         U R                  R                  5         U R                  R                  R                  SS9  U R                  R                  5         U R                  5         U R                  R                  S5        g )Ng      ?)timeoutu-   실행 중: 원격 화면 viewer에서 확인)
rp  r  r]   r  r   r   r  r  r  r   r_   s    r7   rt  Win2RtcApp.start_preview  s|    ""$ ""'''4!!#$$&KLr@   c                n    U R                   R                  5         U R                  R                  S5        g )Nu-   캡처 중지됨. HTTP/WS 서버는 유지됨)r  r   r  r   r_   s    r7   ru  Win2RtcApp.stop_capture  s'      "KLr@   c                l    U R                  5         [        R                  " U R                  5       5        g r   )rt  
webbrowseropenr  r_   s    r7   rv  Win2RtcApp.open_local_viewer  s#    1134r@   c                    U R                   R                  5         U R                   R                  U5        U R                   R                  5         g r   )rF   clipboard_clearclipboard_appendupdater  s     r7   	copy_textWin2RtcApp.copy_text  s4    		!!#		""4(		r@   c                    U R                  5         U R                  5       nU R                  U5        U R                  R	                  U5        U R
                  R	                  S5        g )Nu4   LAN viewer URL을 클립보드에 복사했습니다)rt  r  r  r  r   r  )rR   urls     r7   rw  Win2RtcApp.copy_lan_url  sO    %%'ss#RSr@   c                    U R                  5         U R                  R                  S5        [        R                  " U R
                  SS9R                  5         g )Nu   UPnP 실행 중...TrV   )rt  r  r   rZ   r[   _upnp_workerr]   r_   s    r7   rx  (Win2RtcApp.start_upnp_and_copy_share_url  s@    01 1 1$?EEGr@   c                6  ^ ^^ T R                   R                  T R                  T R                  R                  T R
                  R                  T R                  R                  S9mTR                  (       a  TR                  =(       d    TR                  nU(       d  T R                  n[        UTR                  UTR                  T R                  5      mT R                  R!                  SUU U4S j5        g T R                  R!                  SUU 4S j5        g )N)r  r  r  public_ip_urlr   c                 <   > TR                  T R                  T5      $ r   )_finish_upnp_okr   )r   rR   	share_urls   r7   rA   )Win2RtcApp._upnp_worker.<locals>.<lambda>  s    t';';FNNI'Vr@   c                 :   > TR                  T R                  5      $ r   )_finish_upnp_failr   )r   rR   s   r7   rA   r    s    t'='=fnn'Mr@   )r  
open_portsr  r  r5   r  r  r  r   echo_public_iprouter_external_ipr   http_external_portws_external_portr   rF   r  )rR   public_hostr   r  s   ` @@r7   r  Win2RtcApp._upnp_worker  s    %%;;&&++NN''))11	 & 
 99 //L63L3LK"kk'))''

I IIOOAVWIIOOAMNr@   c                    U R                   R                  U5        U R                  U5        U R                  R                  U S35        g )Nu    / 공유 URL 복사 완료)r  r   r  r  )rR   r   r  s      r7   r  Win2RtcApp._finish_upnp_ok  s<    y)y!wi'BCDr@   c                p    U R                   R                  S5        U R                  R                  U5        g )Nu   UPnP 실패)r  r   r  )rR   r   s     r7   r  Win2RtcApp._finish_upnp_fail  s(    }-G$r@   c                n    U R                   R                  5       nU R                  R                  U5        g r   )r  close_portsr  r   )rR   r  s     r7   ry  Win2RtcApp.close_upnp  s&    ii##%C r@   c                   U R                   R                  5          U R                  R                  5          U R                  R                  5         U R
                  R                  5         U R                  R                  5         U R                  R                  5         U R                  R                  5         U R                  R                  5         [        R                  " S5        g ! [         a     Nf = f! [         a     Nf = f)Nr   )r  r   r  rd   r  r  r   rK   r  r  r  rF   r0  exitr_   s    r7   r  Win2RtcApp.on_close	  s      "	##%	$$&((002 					  		
  		s"   C* 4C: *
C76C7:
DD)+r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r%  r  r  r  r  r  r  r  r  rG   r  rK   r  r  r  r  rF   r  r  r?  r  r  r   r  r  r  )r   r!   r   )r   r   )r  r  )r   r   )r   r  )r  r(   r  r  r   r   )r   r   r   r*  )r   r   r  r   ),r   r   r   r   rS   staticmethodr  r  r  r  r  r  r  r  r  r*  r3  r  r  rs  rr  ra  rd  ri  rj  rk  rm  rn  rl  r  rp  r  rt  ru  rv  r  rw  rx  r  r  r  ry  r  r   r?   r@   r7   r~  r~    s    {'z ! !7Fk
f
?@ *& 
 
S*A~)@

M	M:=?;|H_G7	
:MM5
TH
O,E
%!r@   r~  c                8   [         R                  " SS9nUR                  SSSSS9  UR                  SS	S
S9  UR                  S[        SSS9  UR                  S[        SSS9  UR                  S[        SSS9  UR                  S[        SSS9  UR                  S[        SSS9  UR                  SSSS9  UR                  SSS S!S"9  UR                  S#[         R                  S$S%S&9  UR                  S'S(S)S9  UR                  S*SS+S9  UR                  U 5      $ ),Nz)yjm_win2rtc Windows Remote Desktop Bridge)descriptionz-vz	--verbose
store_trueu   상세 로그)actionhelpz--hostr$   u<   서버 바인딩 주소. 외부/LAN 공유는 0.0.0.0 권장)defaultr  z--http-portr:  u   viewer.html HTTP 포트)r  r  r  z	--ws-portr(  u   JPEG frame WebSocket 포트z--fpsrz  u
   캡처 FPSz	--qualityr{  u   JPEG 품질 1~95z--max-widthr|  u2   전송 전 최대 가로폭. 0이면 원본 크기z--no-auto-portu;   포트 사용 중일 때 자동 다음 포트 탐색을 끔z
--no-tokenr   store_falseu    viewer URL 토큰 사용 안 함)destr  r  z--borderTu2   전송 프레임 자체에 빨간 테두리 표시)r  r  r  z--public-ip-urlzhttp://i.yjm.kr/ip/u   공인 IP 확인 URLz--dump-displaysu4   디스플레이 감지 결과만 출력하고 종료)argparseArgumentParseradd_argumentr(   BooleanOptionalAction
parse_args)argvparsers     r7   r  r    sS   $$1\]F
k,_U
):xy
CD]^
#tB_`
c2LI
#r@RS
CDxy
(  EB  C
7=Oqr

8+I+ISW  _S  T
)3HOef
),E{|T""r@   c                n   [        U 5      nUR                  (       aF  [        [        R                  " 5       5       H#  u  p#[        [        R                  X#5      5        M%     g[        c  [        S[        5        [        S5        g[        R                  " 5       n[        XA5        UR                  5         g)Nr   u0   [ERROR] websockets 패키지를 확인하세요:u5   설치 예: python -m pip install -r requirements.txtrE   )r  dump_displaysr$  r   list_displaysprintr~  r  r   r  r   Tkmainloop)r  r  r  r  rF   s        r7   mainr  ,  s    dD 4 B B DEFC*11#9: F}@BZ[EF557DtMMOr@   __main__)<   )r1   r   r2   r(   r3   r(   r   r(   r   )Tr   
__future__r   r  r   rh  r   r   r  r   r)   r.  r0  rZ   r{   r  	functoolsr   http.serverr   r   pathlibr   urllib.parser   r	   tkinterr   r
   r[  PILr   r   ro   yjm_capture_sourcesr   yjm_monitor_prober   yjm_net_upnpr   r   r  r   yjm_remote_statusr   yjm_file_transferr   yjm_clipboard_syncr   host.protocolr   r   r   r   yjm_settingsr   r   r   r   yjm_ai_snapshot_ringr   websockets.asyncio.serverr   websockets.datastructuresr   websockets.http11r    r  rd   r   r"   __annotations__Lockr   r8   r:   r   r,  r<  r~  r  r  r   
SystemExitr?   r@   r7   <module>r     s6   #   	   	    
     E  +   
    4 / 8 4 3 8 3 _ _ T T 3
$/1*  $ !z  NN	yJ JZQ, Q,h BM; M;`p pf#"$ z
TV
 G   #EGH"	#s   ?E EEE