-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathKeyZones.cmd
More file actions
315 lines (288 loc) · 15.9 KB
/
Copy pathKeyZones.cmd
File metadata and controls
315 lines (288 loc) · 15.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
/*&cls&@echo off&Title "%~dpnx0" & REM SEE // USER CUSTOMISATION * BELOW if you wish to make changes before running this file
cd /d "%~dp0" & echo Compiling "%~dpn0.exe"
set "CSC=%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\csc.exe"
if not exist "%CSC%" echo Compiler not found & pause & exit /b
::Prepare the Icon/BMP/ICO/PNG graphics as a 24 px X 24 px RAW PNG.Base64
>icon.b64 echo iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAqElEQVRIiWNU2ujDQEvAgi5wz3/Lf0oMVNrow4jXAgYGBob/p97vJsdwRjNBV3QxrBYwMDAwMJgKYCjGC05/wOooJpIMIQPQ3ALcQcTAwMDIyIhPGg7+/8edLvBagE8jsYCFgYGB4e75BrhJjAxbKDIQ2SxlwwbGoR9Eo6mIIBgmQaRs2IBsElw1OUGEZtZwCSJyNBILBjCIcNRQVLEAW91KLmCkdbMFAOxQOQrYL9gMAAAAAElFTkSuQmCC
::Convert first into an App.ico and keep base64 for internal conversion
>makeico.cs echo using System; using System.IO; class M { static void Main() {
>>makeico.cs echo var p = Convert.FromBase64String(File.ReadAllText("icon.b64")); using (var f = File.Create("app.ico")) { f.Write(new byte[]{0,0,1,0,1,0,24,24,0,0,1,0,32,0},0,14); W(f,p.Length); W(f,22); f.Write(p,0,p.Length); } } static void W(Stream s,int v){s.WriteByte((byte)v);s.WriteByte((byte)(v^>^>8));s.WriteByte((byte)(v^>^>16));s.WriteByte((byte)(v^>^>24)); } }
"%CSC%" /nologo makeico.cs && makeico.exe && del makeico.cs makeico.exe
:: The app.ico AND Title icon.b64 can now be used by main compilation
"%CSC%" /nologo /target:winexe /win32icon:app.ico /resource:icon.b64 /platform:x86 /out:"%~dpn0.exe" "%~dpnx0"
:: It should now be safe to delete the temporary graphics
del app.ico icon.b64
REM IMPORTANT we must pause and exit here before NOTES
pause & exit /b
NOTES:
This Hybrid file is a working demonstration of SumatraPDF Plugin Overlay "On canvas" it compiles to an exe that can
follow the canvas area. It is simply providing two click zones right and left "On Canvas" Key stroke actions.
You may use this concept many other ways, but this is simply a demonstration for Windows 7+!
Simply bind the compiled exe to run shortcuts in SumatraPDF settings. Like this: where a b and c d are the keys you wish to action
These are single or multiple combinations, just like sendkeys.
ExternalViewers [
[
CommandLine = C:\path to your version\KeyZones.exe L="a,b" R="c,d"
Name = Hot &Zones Overlay
Key = z
ToolbarSvgIcon = <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><rect width="24" height="24" fill="none"/><rect x="5" y="0" width="15" height="24" fill="#fff" stroke="#000" stroke-width="1"/><rect x="1" y="8" width="3" height="8" fill="#f88"/><path d="M 7 6 L 18 6 M 7 9 L 18 9 M 7 12 L 18 12 M 7 15 L 18 15 M 7 18 L 18 18" stroke="#888"/><rect x="21" y="8" width="3" height="8" fill="#f88"/></svg>
]
]
*/
using System;using System.Drawing;using System.Text;using System.Windows.Forms;
using System.Runtime.InteropServices;using System.IO;using System.Reflection;
class Overlay : Form
{
// ********************
// USER CUSTOMISATION *
// ********************
public static string LEFT_HOTKEY = "p"; // default, can be overridden by L="..."
public static string RIGHT_HOTKEY = "n"; // default, can be overridden by R="..."
public static int OVERLAY_OPACITY = 25; // This is so you know where the Zones are but once trialed can be reduced
public static int HOTZONE_HEIGHT = 240; // Reduce as much as you wish but below 60 will be hard to avoid the close area
public static int HOTZONE_WIDTH = 50; // to provide a wide enough area keep above 20
public static Color HOTZONE_COLOR = Color.FromArgb(120,255,120); // This is a grey green tint but you can alter to suite a theme or mood
// You should not need to change these
public static Color CLOSE_COLOR = Color.Red;
public static int CLOSE_SIZE = 20;
public static int ANTI_JITTER = 2;
public static Color TRANSPARENT_KEY = Color.Magenta;
// INTERNAL
[DllImport("user32.dll")] static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
const uint WM_KEYDOWN = 0x0100; const uint WM_KEYUP = 0x0101;
[DllImport("user32.dll")] static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll")] static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll")] static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")] static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", CharSet = CharSet.Auto)] static extern IntPtr FindWindow(string c, string n);
[DllImport("user32.dll")] static extern IntPtr FindWindowEx(IntPtr parent, IntPtr childAfter, string className, string windowName);
[DllImport("user32.dll")] static extern bool GetWindowRect(IntPtr h, out RECT r);
[DllImport("user32.dll")] static extern IntPtr WindowFromPoint(POINT pt);
[DllImport("user32.dll")] static extern bool SetWindowPos(IntPtr h, IntPtr ins, int X, int Y, int W, int H, uint f);
const int WH_MOUSE_LL = 14; const int WM_LBUTTONDOWN = 0x201; const uint SWP = 0x40 | 0x10; static readonly IntPtr TOP = new IntPtr(-1);
[StructLayout(LayoutKind.Sequential)] public struct POINT { public int x, y; }
[StructLayout(LayoutKind.Sequential)] struct MSLLHOOKSTRUCT { public POINT pt; public uint mouseData; public uint flags; public uint time; public IntPtr dwExtraInfo; }
struct RECT { public int L, T, R, B; }
delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
static LowLevelMouseProc mouseProc; static IntPtr mouseHook = IntPtr.Zero;
Timer poll = new Timer();
RECT lastOverlay; bool hasOverlay = false;
Rectangle leftBarZone; Rectangle rightBarZone;
// MAIN
[STAThread]
static void Main(string[] args)
{
// Defaults already set in user area but allow complex overrides via args: L="..." R="..."
foreach (var a in args)
{
if (a.StartsWith("L=", StringComparison.OrdinalIgnoreCase))
LEFT_HOTKEY = a.Substring(2).Trim('"');
if (a.StartsWith("R=", StringComparison.OrdinalIgnoreCase))
RIGHT_HOTKEY = a.Substring(2).Trim('"');
}
Application.EnableVisualStyles(); Application.Run(new Overlay());
}
// FORM SETUP
protected override CreateParams CreateParams {
get {
const int WS_EX_LAYERED = 0x80000;
const int WS_EX_TRANSPARENT = 0x20;
var cp = base.CreateParams;
cp.ExStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT;
return cp;
}
}
protected override void OnFormClosed(FormClosedEventArgs e) {
if (mouseHook != IntPtr.Zero) UnhookWindowsHookEx(mouseHook); base.OnFormClosed(e);
}
public Overlay() {
// Load icon from embedded base64
var asm = Assembly.GetExecutingAssembly(); Image icon; using (var s = asm.GetManifestResourceStream("icon.b64"))
using (var r = new StreamReader(s)) {
byte[] png = Convert.FromBase64String(r.ReadToEnd()); using (var ms = new MemoryStream(png)) icon = Image.FromStream(ms);
}
using (var bmp = new Bitmap(icon)) this.Icon = Icon.FromHandle(bmp.GetHicon());
IntPtr c = FindCanvas(); if (c == IntPtr.Zero) { Close(); return; }
FormBorderStyle = FormBorderStyle.None; ShowInTaskbar = true; TopMost = true;
BackColor = TRANSPARENT_KEY; TransparencyKey = TRANSPARENT_KEY; Opacity = OVERLAY_OPACITY / 100.0;
poll.Interval = 500; poll.Tick += (s,e)=>UpdateOverlay(); poll.Start(); // Cause of problems as it starts from the off
mouseProc = MouseHookCallback; mouseHook = SetWindowsHookEx(WH_MOUSE_LL, mouseProc, GetModuleHandle(null), 0);
}
// FIND SUMATRA CANVAS
IntPtr FindCanvas()
{
IntPtr frame = FindWindow("SUMATRA_PDF_FRAME", null);
if (frame == IntPtr.Zero) frame = FindWindow("SumatraPDF", null);
IntPtr canvas = IntPtr.Zero;
if (frame != IntPtr.Zero) canvas = FindWindowEx(frame, IntPtr.Zero, "SUMATRA_PDF_CANVAS", null);
// If no canvas, ask user what to do
if (canvas == IntPtr.Zero)
{
// ⭐ FIX: if Sumatra is NOT ACTIVE, STOP TIMER and restart after the user says yes start timer
poll.Stop();
var choice = MessageBox.Show(
"SumatraPDF canvas not active.\n\nNO = abort overlay now\nYES = load a document then continue",
"Canvas Missing", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (choice == DialogResult.No) return IntPtr.Zero; // abort overlay
// User chose YES allow them to fix state Try again immediately
frame = FindWindow("SUMATRA_PDF_FRAME", null);
if (frame == IntPtr.Zero) frame = FindWindow("SumatraPDF", null); poll.Start();
if (frame != IntPtr.Zero) canvas = FindWindowEx(frame, IntPtr.Zero, "SUMATRA_PDF_CANVAS", null);
// Fail if no canvas
if (canvas == IntPtr.Zero) return IntPtr.Zero;
}
return canvas; // SUCCESS: return handle
}
// MOUSE HOOK
static IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
if (nCode >= 0) {
int msg = wParam.ToInt32();
MSLLHOOKSTRUCT data = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
Overlay self = Application.OpenForms.Count > 0 ? Application.OpenForms[0] as Overlay : null;
if (self != null) self.HandleMouse(msg, data.pt);
}
return CallNextHookEx(mouseHook, nCode, wParam, lParam);
}
void HandleMouse(int msg, POINT pt) {
if (!hasOverlay) return;
Point p = new Point(pt.x, pt.y);
IntPtr frame = FindWindow("SUMATRA_PDF_FRAME", null);
if (frame == IntPtr.Zero) frame = FindWindow("SumatraPDF", null); IntPtr canvas = IntPtr.Zero;
if (frame != IntPtr.Zero) canvas = FindWindowEx(frame, IntPtr.Zero, "SUMATRA_PDF_CANVAS", null);
IntPtr underMouse = WindowFromPoint(pt);
Rectangle closeBox = new Rectangle(lastOverlay.L, lastOverlay.T, CLOSE_SIZE, CLOSE_SIZE);
if (msg == WM_LBUTTONDOWN && closeBox.Contains(p)) { Application.Exit(); return; }
if (msg == WM_LBUTTONDOWN && leftBarZone.Contains(p)) { if (!onCanvas(frame, canvas, underMouse)) return; SendHotKey(LEFT_HOTKEY); return; }
if (msg == WM_LBUTTONDOWN && rightBarZone.Contains(p)) { if (!onCanvas(frame, canvas, underMouse)) return; SendHotKey(RIGHT_HOTKEY); return; }
}
bool onCanvas(IntPtr frame, IntPtr canvas, IntPtr underMouse)
{
if (underMouse == frame || underMouse == canvas) return true;
return new offCanvasMsg().ShowDialog() == DialogResult.Yes;
}
public class offCanvasMsg : Form
{
public offCanvasMsg()
{
Text = "Different App Detected"; Width = 300; Height = 200; TopMost = true; // IMPORTANT
StartPosition = FormStartPosition.CenterScreen;
var label = new Label()
{
Text = "You clicked a KeyZone, but Windows detected\n a different app under the cursor.\n\n" +
"YES = send keys anyway\nNO = return without send\nOr else click Zone close symbol",
Dock = DockStyle.Fill, TextAlign = ContentAlignment.MiddleCenter
};
var yes = new Button() { Text = "Yes", DialogResult = DialogResult.Yes, Dock = DockStyle.Bottom };
var no = new Button() { Text = "No", DialogResult = DialogResult.No, Dock = DockStyle.Bottom };
Controls.Add(label); Controls.Add(yes); Controls.Add(no); AcceptButton = yes; CancelButton = no;
}
}
// KEY PARSER
static void ParseKey(string key, out int vk, out bool ctrl, out bool alt, out bool shift)
{
string lower = key.ToLower();
ctrl = lower.Contains("ctrl+");
alt = lower.Contains("alt+");
shift = lower.Contains("shift+");
string k = lower;
k = k.Replace("ctrl+", "");
k = k.Replace("alt+", "");
k = k.Replace("shift+", "");
k = k.Trim();
vk = 0;
if (k == "left") vk = 0x25;
else if (k == "up") vk = 0x26;
else if (k == "right") vk = 0x27;
else if (k == "down") vk = 0x28;
else if (k == "pgup") vk = 0x21;
else if (k == "pgdn") vk = 0x22;
else if (k == "home") vk = 0x24;
else if (k == "end") vk = 0x23;
else if (k == "tab") vk = 0x09;
else if (k == "esc") vk = 0x1B;
else if (k.StartsWith("f"))
{
int fn;
if (int.TryParse(k.Substring(1), out fn))
{
if (fn >= 1 && fn <= 12)
vk = 0x70 + (fn - 1);
}
}
else if (k.Length == 1)
{
vk = Char.ToUpper(k[0]);
}
}
// SEND HOTKEY (SEQUENCES)
void SendHotKey(string key)
{
// IntPtr f = FindWindow("SUMATRA_PDF_FRAME", null);
// if (f == IntPtr.Zero) f = FindWindow("SumatraPDF", null);
// if (f == IntPtr.Zero) return;
IntPtr f = FindCanvas(); if (f == IntPtr.Zero) return;
string[] parts = key.Split(',');
foreach (string part in parts)
{
int vk;
bool ctrl, alt, shift;
ParseKey(part.Trim(), out vk, out ctrl, out alt, out shift);
if (vk == 0) continue;
if (ctrl) PostMessage(f, WM_KEYDOWN, (IntPtr)0x11, IntPtr.Zero);
if (alt) PostMessage(f, WM_KEYDOWN, (IntPtr)0x12, IntPtr.Zero);
if (shift) PostMessage(f, WM_KEYDOWN, (IntPtr)0x10, IntPtr.Zero);
PostMessage(f, WM_KEYDOWN, (IntPtr)vk, IntPtr.Zero);
PostMessage(f, WM_KEYUP, (IntPtr)vk, IntPtr.Zero);
if (shift) PostMessage(f, WM_KEYUP, (IntPtr)0x10, IntPtr.Zero);
if (alt) PostMessage(f, WM_KEYUP, (IntPtr)0x12, IntPtr.Zero);
if (ctrl) PostMessage(f, WM_KEYUP, (IntPtr)0x11, IntPtr.Zero);
}
}
// DRAW VARIABLE OVERLAY
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
int W = Width, H = Height, mid = H / 2;
int barH = HOTZONE_HEIGHT;
int barY = mid - (barH / 2);
leftBarZone = new Rectangle(
lastOverlay.L,
lastOverlay.T + barY,
HOTZONE_WIDTH,
barH);
rightBarZone = new Rectangle(
lastOverlay.R - HOTZONE_WIDTH,
lastOverlay.T + barY,
HOTZONE_WIDTH,
barH);
Rectangle leftBar = new Rectangle(0, barY, HOTZONE_WIDTH, barH);
Rectangle rightBar = new Rectangle(W - HOTZONE_WIDTH, barY, HOTZONE_WIDTH, barH);
using (var band = new SolidBrush(HOTZONE_COLOR))
{
g.FillRectangle(band, leftBar);
g.FillRectangle(band, rightBar);
}
using (var pen = new Pen(CLOSE_COLOR, 4))
{
g.DrawLine(pen, 0, 0, CLOSE_SIZE, CLOSE_SIZE);
g.DrawLine(pen, CLOSE_SIZE, 0, 0, CLOSE_SIZE);
}
}
// POSITION OVERLAY
void UpdateOverlay() {
IntPtr c = FindCanvas();
if (c == IntPtr.Zero) { poll.Stop(); Application.Exit(); return; }
RECT r;
if (!GetWindowRect(c, out r)) return;
int W = r.R - r.L; int H = r.B - r.T; int autoY = r.T + (H / 2 - HOTZONE_HEIGHT / 2);
RECT newRect = new RECT { L = r.L, T = autoY, R = r.L + W, B = autoY + HOTZONE_HEIGHT };
if (hasOverlay) {
bool changed =
Math.Abs(newRect.L - lastOverlay.L) > ANTI_JITTER || Math.Abs(newRect.R - lastOverlay.R) > ANTI_JITTER ||
Math.Abs(newRect.T - lastOverlay.T) > ANTI_JITTER || Math.Abs(newRect.B - lastOverlay.B) > ANTI_JITTER;
if (!changed) return;
}
SetWindowPos(Handle, TOP, newRect.L, newRect.T, W, HOTZONE_HEIGHT, SWP);
lastOverlay = newRect; hasOverlay = true; Invalidate();
}
}