Processing Ajax...

Title

Message

Confirm

Confirm

Confirm

Confirm

Are you sure you want to delete this item?

Confirm

Are you sure you want to delete this item?

Confirm

Are you sure?

Toggle Fade Selected Monitor

Description
Fades the monitor chosen from the select box to black over 0.2 seconds. Running it again will unfade the monitor.
Language
C#.net
Minimum Version
Created By
sy Lee
Contributors
-
Date Created
13d ago
Date Last Modified
13d ago

Scripted Function (Macro) Code

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.Collections.Generic;

// 이 스크립트는 Thomas의 '주 모니터를 제외한 모든 모니터 어둡게 하기(Dim All Monitors Except Primary)' 기능을 기반으로 제작된 NetMage의 '주 모니터를 제외한 모든 모니터를 검은색으로 만들기' 기능을 전적으로 참고하여 작성되었습니다."
// This script is entirely based on NetMage's 'Blacken All Monitors Except Primary' function, which is based on Thomas's 'Dim All Monitors Except Primary' function.

public static class DisplayFusionFunction {
    private static readonly string SettingName = "BlackenMonitors_Run";
    public static void Run(IntPtr windowHandle) {
        // 설정값을 '실행 중'에서 '중단'으로, 또는 그 반대로 전환(토글)합니다.
        // Toggles the setting value from 'running' to 'stopped', or vice versa.
        ToggleSetting();

        if (RunFadeMonitors()) {
            // 모니터 선택 창을 띄웁니다.
            // Opens the monitor selection window.
            using (var selector = new MonitorSelectorForm()) {
                var result = selector.ShowDialog();
                
                if (result == DialogResult.OK && selector.SelectedMonitorIds.Count > 0) {
                    // 선택한 모니터들을 관리 목록에 추가합니다.
                    // Adds the selected monitors to the management list.
                    var forms = new List<Form>();
                    foreach (uint monitorId in selector.SelectedMonitorIds) {
                        forms.Add(new TransparentForm(monitorId));
                    }
                
                    // 사용자 정의 애플리케이션 컨텍스트를 사용하여 목록에 추가된 폼(검은 화면)을 엽니다.
                    // Opens the forms (black screens) added to the list using a custom application context.
                    Application.Run(new MultipleFormApplicationContext(forms));
                } else {
                    // 취소했거나 선택된 모니터가 없으면 설정을 다시 원래대로(꺼짐 상태로) 돌려놓습니다.
                    // If cancelled or no monitor is selected, reverts the setting to its original state (off).
                    ToggleSetting();
                }
            }
        }
    }
    
    // 스크립트의 현재 실행 상태(켜짐/꺼짐)를 확인하는 함수입니다.
    // Function to check the current execution status (on/off) of the script.
    private static bool RunFadeMonitors() {
        return BFS.ScriptSettings.ReadValue(SettingName) == "run";
    }

    // 스크립트 설정을 '실행 중' 또는 '중단'으로 전환하는 함수입니다.
    // Function to toggle the script setting to 'running' or 'stopped'.
    private static void ToggleSetting() {
        BFS.ScriptSettings.WriteValue(SettingName, RunFadeMonitors() ? "not" : "run");
    }

    // 여러 개의 폼(창)을 여는 것을 지원하도록 클래스를 확장합니다.
    // Extends the class to support opening multiple forms (windows).
    private class MultipleFormApplicationContext : ApplicationContext {
        internal MultipleFormApplicationContext(List<Form> forms) {
            // 각 폼을 열고, 폼이 닫힐 때 발생하는 이벤트를 추가합니다.
            // Opens each form and adds an event that occurs when the form is closed.
            foreach(Form form in forms) {
                form.FormClosed += OnFormClosed;
                form.Show();
            }
        }

        // 모든 폼이 닫히면 애플리케이션을 안전하게 종료합니다.
        // Safely exits the application when all forms are closed.
        private void OnFormClosed(object sender, EventArgs e) {
            if(Application.OpenForms.Count == 0)
                ExitThread();
        }
    }

    // 모니터 선택을 위한 폼 클래스
    // Form class for monitor selection.
    private class MonitorSelectorForm : Form {
        public List<uint> SelectedMonitorIds { get; private set; }
        private FlowLayoutPanel panel;
        private Button btnOk;
        private Button btnCancel;

        public MonitorSelectorForm() {
            SelectedMonitorIds = new List<uint>();
            InitializeComponent();
        }

        private void InitializeComponent() {
			// 제목
			// Title
            this.Text = "선택";
            // 고정 사이즈 제거 및 AutoSize 설정
            // Remove fixed size and set AutoSize.
            this.AutoSize = true;
            this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
            this.StartPosition = FormStartPosition.CenterScreen;
            this.FormBorderStyle = FormBorderStyle.FixedDialog;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            // 폼 패딩 제거 (TableLayoutPanel에서 처리)
            // Remove form padding (handled in TableLayoutPanel).
            this.Padding = new Padding(0); 

            // 메인 레이아웃 패널 (TableLayoutPanel 사용으로 중앙 정렬)
            // Main layout panel (Use TableLayoutPanel for center alignment).
            TableLayoutPanel mainLayout = new TableLayoutPanel();
            mainLayout.AutoSize = true;
            mainLayout.AutoSizeMode = AutoSizeMode.GrowAndShrink;
            mainLayout.ColumnCount = 1;
            mainLayout.RowCount = 2;
            mainLayout.Padding = new Padding(10); // 10px 여백 (10px padding)
            
            // 모니터 리스트 패널
            // Monitor list panel.
            panel = new FlowLayoutPanel();
            panel.AutoSize = true;
            panel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
            panel.FlowDirection = FlowDirection.TopDown;
            panel.WrapContents = false;
            panel.Margin = new Padding(0, 0, 0, 20); // 버튼과의 간격 (Spacing with buttons)
            // 체크박스들을 중앙 정렬하기 위해 Anchor 설정은 FlowLayout 내부에서 적용되지 않음.
            // 하지만 panel 자체가 TableLayout 내에서 중앙 정렬됨.
            
            // 모든 모니터 ID를 가져와서 체크박스 생성
            // Get all monitor IDs and create checkboxes.
            uint[] monitorIds = BFS.Monitor.GetMonitorIDs();
            int index = 1;
            foreach (uint id in monitorIds) {
                CheckBox cb = new CheckBox();
                // 해상도 정보 제거
                // Remove resolution information.
                cb.Text = $"Monitor {index}";
                cb.Tag = id;
                cb.AutoSize = true;
                cb.Margin = new Padding(3);
                panel.Controls.Add(cb);
                index++;
            }

            // 버튼 패널 (수평 정렬)
            // Button panel (horizontal alignment).
            FlowLayoutPanel buttonPanel = new FlowLayoutPanel();
            buttonPanel.FlowDirection = FlowDirection.LeftToRight;
            buttonPanel.AutoSize = true;
            buttonPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
            buttonPanel.WrapContents = false;
            buttonPanel.Margin = new Padding(0);
			
			//확인/취소버튼
			//OK/Cancel botton
            btnOk = new Button();
            btnOk.Text = "확인";
            btnOk.DialogResult = DialogResult.OK;
            btnOk.AutoSize = true;
            btnOk.Margin = new Padding(0, 0, 10, 0); // 버튼 사이 간격 (Spacing between buttons)
            btnOk.Click += BtnOk_Click;

            btnCancel = new Button();
            btnCancel.Text = "취소";
            btnCancel.DialogResult = DialogResult.Cancel;
            btnCancel.AutoSize = true;
            btnCancel.Margin = new Padding(0);

            buttonPanel.Controls.Add(btnOk);
            buttonPanel.Controls.Add(btnCancel);

            // 메인 레이아웃에 추가 (Anchor = None으로 설정하여 셀 내에서 중앙 정렬)
            // Add to main layout (Set Anchor = None to center align within the cell).
            mainLayout.Controls.Add(panel, 0, 0);
            panel.Anchor = AnchorStyles.None;
            
            mainLayout.Controls.Add(buttonPanel, 0, 1);
            buttonPanel.Anchor = AnchorStyles.None;

            this.Controls.Add(mainLayout);
            
            this.AcceptButton = btnOk;
            this.CancelButton = btnCancel;
        }

        private void BtnOk_Click(object sender, EventArgs e) {
            SelectedMonitorIds.Clear();
            foreach (Control c in panel.Controls) {
                if (c is CheckBox cb && cb.Checked) {
                    SelectedMonitorIds.Add((uint)cb.Tag);
                }
            }
        }
    }
    
    // 검은색 화면을 구현하기 위해 폼 클래스를 확장합니다.
    // Extends the Form class to implement the black screen.
    private class TransparentForm : Form {
        private uint MonitorId; // 어떤 모니터에 띄울지 결정 (Decides which monitor to display on)
        private uint FadeWait;  // 어두워지는 속도 대기 시간 (Fade speed wait time)
        private decimal TransparentIncrement; // 투명도 변화 단계 (Transparency change step)
        private IntPtr HandleTHREADSAFE; // 스레드 안전한 핸들값 (Thread-safe handle value)
        private decimal Transparency; // 현재 투명도 상태 (Current transparency state)
        
        internal TransparentForm(uint monitorId) {		
            MonitorId = monitorId;
            Transparency = 0m;
            const decimal FadeTime = 0.2m; // 어두워지는 데 걸리는 시간 (0.2초) (Time to fade: 0.2s)
            TransparentIncrement = 10m; // 투명도 변화 단위 (10%) (Transparency change unit: 10%)
            FadeWait = (uint)(TransparentIncrement * 10m * FadeTime);
            
            SuspendLayout();
            
            // 폼 레이아웃 설정: 검은색 배경, 테두리 없음, 작업표시줄 표시 안 함
            // Form layout settings: Black background, no border, do not show in taskbar.
            BackColor = Color.Black;
            FormBorderStyle = FormBorderStyle.None;
            ShowInTaskbar = false;

            // 해당 모니터의 크기와 위치에 맞게 창 배치
            // Position the window according to the size and location of the monitor.
            Rectangle bounds = BFS.Monitor.GetMonitorBoundsByID(MonitorId);
            Location = new Point(bounds.X, bounds.Y);
            Size = new Size(bounds.Width, bounds.Height);
            StartPosition = FormStartPosition.Manual;
            
            // 창을 항상 위로 설정하고 투명도 적용 시작
            // Set the window to always be on top and start applying transparency.
            BFS.Window.SetAlwaysOnTop(Handle, true);
            BFS.Window.SetTransparency(Handle, Transparency);

            // 폼이 로드될 때 실행될 이벤트 설정
            // Set the event to be executed when the form is loaded.
            Load += Form_Load;			
            
            ResumeLayout(false);
        }
        
        private void Form_Load(object sender, EventArgs e) {
            HandleTHREADSAFE = Handle;
            
            // 윈도우 스타일을 설정하여 이 창이 사용자 입력(클릭 등)을 무시하고 통과시키도록 만듭니다.
            // (터치스크린이어도 이 창 뒤에 있는 앱을 조작할 수 있게 해줍니다.)
            // Sets the window style so that this window ignores and passes through user input (clicks, etc.).
            // (Allows manipulation of apps behind this window even with a touchscreen.)
            uint style = (uint)BFS.Window.GetWindowStyleEx(Handle) | (uint)BFS.WindowEnum.WindowStyleEx.WS_EX_TRANSPARENT | (uint)BFS.WindowEnum.WindowStyleEx.WS_EX_LAYERED;
            BFS.Window.SetWindowStyleEx((BFS.WindowEnum.WindowStyleEx)style, Handle);
            
            // 종료 이벤트를 감시하기 위한 별도의 스레드를 시작합니다.
            // Starts a separate thread to monitor the exit event.
            new Thread(new ThreadStart(ExitListener)).Start();
        }

        private void ExitListener() {
            try {
              while(true) {
                  if(RunFadeMonitors()) {
                      // 점점 어둡게 만들기 (Fade to Black)
                      if (Transparency < 100m) {
                          Transparency += TransparentIncrement;
                          BFS.Window.SetTransparency(HandleTHREADSAFE, Transparency);
                      }
                      BFS.General.ThreadWait((Transparency < 100m) ? FadeWait : 250u);				
                  }
                  else {
                      if (this.Transparency > 0m) {
                          // 점점 밝게 만들기 (Fade back in)
                          Transparency -= TransparentIncrement;
                          BFS.Window.SetTransparency(HandleTHREADSAFE, Transparency);
                          BFS.General.ThreadWait((Transparency < 100m) ? FadeWait : 250u);
                      }
                      else {
                          // 완전히 밝아지면 창을 닫고 스레드 종료
                          // When fully bright, close the window and end the thread.
                          try {
                              this.Invoke((MethodInvoker) delegate {
                                  Close();
                              });
                          } catch { }
                          break;
                      }
                  }
              }
            } catch { }
        }
    }
}