1
+ // Compile this way:
2
+ // gcc -framework cocoa -x objective-c -o quickgrab quickgrab.m
3
+ //
4
+
5
+ #define MYLog (...) do { if (__builtin_expect (kDebuggingEnabled , 0 )) { NSLog (__VA_ARGS__); } } while (0 )
6
+
7
+ #import < Cocoa/Cocoa.h>
8
+
9
+ // we start with debugging off...
10
+ bool kDebuggingEnabled = NO ;
11
+
12
+
13
+
14
+ // Neat NSLog like function to stdout...
15
+ // http://stackoverflow.com/a/3487392/348694
16
+ void NSPrint (NSString *format, ...) {
17
+ va_list args;
18
+ va_start (args, format);
19
+
20
+ fputs ([[[[NSString alloc ] initWithFormat: format arguments: args] autorelease ] UTF8String ], stdout);
21
+ fputs (" \n " , stdout);
22
+ va_end (args);
23
+ }
24
+
25
+
26
+ void showWindowList (int pid);
27
+ void showWindowList (int pid)
28
+ {
29
+ MYLog (@" List of windows available for capture..." );
30
+
31
+ CGWindowListOption listOptions = kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements ;
32
+ CFArrayRef windowList = CGWindowListCopyWindowInfo (listOptions, kCGNullWindowID );
33
+ MYLog (@" Found %d windows\n " , CFArrayGetCount (windowList));
34
+ // MYLog(@"%@", windowList);
35
+
36
+ // Lets walk through the list
37
+ for (NSMutableDictionary * entry in (NSArray *)windowList)
38
+ {
39
+ NSString *ownerName = [entry objectForKey: (id )kCGWindowOwnerName ];
40
+ NSInteger ownerPID = [[entry objectForKey: (id )kCGWindowOwnerPID ] integerValue ];
41
+ NSString *name = [entry objectForKey: (id )kCGWindowName ];
42
+ NSNumber *wnumber = [entry objectForKey: (id )kCGWindowNumber ];
43
+ NSNumber *wlevel = [entry objectForKey: (id )kCGWindowLayer ];
44
+
45
+ // Show all or specific app windows at 0 Level only
46
+ if ( (pid == 0 || ownerPID == pid ) && [wlevel integerValue ] == 0 )
47
+ NSPrint (@" App: %@ , PID: %d , Window ID: %d , Window Title: %@ " , \
48
+ ownerName, ownerPID, [wnumber integerValue ], name);
49
+
50
+ }
51
+
52
+ }
53
+
54
+ void showHelp (void );
55
+ void showHelp (void )
56
+ {
57
+ printf (" usage: quickgrab [-pid <id>] [-winid <id>] [-showlist yes] [-debug yes] -file <file> \n \
58
+ -pid <id> Process ID of application that you want to target. If there are \n \
59
+ multiple windows, the first, as ordered by the system will be captured. \n \
60
+ -winid <id> Window ID you want to capture. To get the ID use -showlist option. \n \
61
+ -showlist yes Lists available windows with the Process IDs and Window IDs to use \n \
62
+ with the other options.\n \
63
+ -debug yes Enables output of debugging information. \n \
64
+ -file <file> Where to save the image. \n \
65
+ \n \
66
+ It captures the top most window of the active application unless -pid and/or -winid \n \
67
+ options are supplied. \n \
68
+ \n \
69
+ \n \
70
+ Examples \n \
71
+ \n \
72
+ Capture the top window of active application after 2 seconds.\n \
73
+ \n \
74
+ $ sleep 2 ; ./quickgrab -file activewindow.png\n \
75
+ \n \
76
+ Taking continuous shots of top window every 2 seconds\n \
77
+ \n \
78
+ $ while true; do ./quickgrab -file topwindow.png ; sleep 2 ; done\n \
79
+ \n \
80
+ Like above but creating a new file for every shot with date/time as the filename...\n \
81
+ \n \
82
+ $ while true; do ./quickgrab -file `date \" +%% Y%% m%% d%% H%% M%% S\" `.png ; sleep 2 ; done\n \
83
+ " );
84
+ }
85
+
86
+
87
+ void grabWindow (int winid, NSString *filename);
88
+ void grabWindow (int winid, NSString *filename)
89
+ {
90
+ MYLog (@" Getting image of window id: %d " , winid);
91
+
92
+ CGImageRef cgImage = CGWindowListCreateImage (CGRectNull , \
93
+ kCGWindowListOptionIncludingWindow , winid, kCGWindowImageDefault );
94
+
95
+ if (cgImage == NULL )
96
+ exit (3 );
97
+
98
+ MYLog (@" Image created..." );
99
+
100
+ // Create a bitmap rep from the image...
101
+ NSBitmapImageRep *bitmapRep = [[NSBitmapImageRep alloc ] initWithCGImage: cgImage];
102
+
103
+ // Save the file
104
+ NSData *data = [bitmapRep representationUsingType: NSPNGFileType properties: nil ];
105
+ [data writeToFile: filename atomically: NO ];
106
+
107
+ CGImageRelease (cgImage);
108
+
109
+ MYLog (@" Image saved to %@ " , filename);
110
+ }
111
+
112
+
113
+
114
+ int main (int argc, char *argv[])
115
+ {
116
+ id pool=[NSAutoreleasePool new ];
117
+
118
+ // Get command line arguments
119
+ NSUserDefaults *args = [NSUserDefaults standardUserDefaults ];
120
+ NSString *fileArg = [args stringForKey: @" file" ];
121
+ NSInteger pidArg = [args integerForKey: @" pid" ];
122
+ NSInteger widArg = [args integerForKey: @" winid" ];
123
+ BOOL listArg = [args boolForKey: @" showlist" ];
124
+ BOOL debugArg = [args boolForKey: @" debug" ];
125
+
126
+ // Enable debugging if user asks
127
+ if ( debugArg == 1 )
128
+ {
129
+ kDebuggingEnabled = YES ;
130
+ }
131
+
132
+ MYLog (@" %@ " , [[NSProcessInfo processInfo ] arguments ]);
133
+ MYLog (@" Output File(fileArg) = %@ " , fileArg);
134
+ MYLog (@" Process ID(pidArg) = %d " , pidArg);
135
+ MYLog (@" Window ID(widArg) = %d " , widArg);
136
+ MYLog (@" Show Window List = %d " , listArg);
137
+ MYLog (@" Enable Debugging = %d " , debugArg);
138
+
139
+ // if ( listArg )
140
+ // {
141
+ // showWindowList(pidArg);
142
+ // exit(0);
143
+ // }
144
+
145
+ // Stop, nothing to do!
146
+ if ( fileArg == nil && !listArg)
147
+ {
148
+ showHelp ();
149
+ exit (1 );
150
+ }
151
+
152
+ MYLog (@" Getting list of windows available for capture..." );
153
+
154
+ CGWindowListOption listOptions = kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements ;
155
+ CFArrayRef windowList = CGWindowListCopyWindowInfo (listOptions, kCGNullWindowID );
156
+ MYLog (@" Found %d windows\n " , CFArrayGetCount (windowList));
157
+ // MYLog(@"%@", windowList);
158
+
159
+ // Lets walk through the list
160
+ for (NSMutableDictionary * entry in (NSArray *)windowList)
161
+ {
162
+ NSString *ownerName = [entry objectForKey: (id )kCGWindowOwnerName ];
163
+ NSInteger ownerPID = [[entry objectForKey: (id )kCGWindowOwnerPID ] integerValue ];
164
+ NSString *name = [entry objectForKey: (id )kCGWindowName ];
165
+ NSNumber *wnumber = [entry objectForKey: (id )kCGWindowNumber ];
166
+ NSNumber *wlevel = [entry objectForKey: (id )kCGWindowLayer ];
167
+
168
+ // Only interested on windows at 0 level only
169
+ if ([wlevel integerValue ] == 0 )
170
+ if ( listArg )
171
+ {
172
+ if (pidArg == 0 || ownerPID == pidArg )
173
+ NSPrint (@" App: %@ , PID: %d , Window ID: %d , Window Title: %@ " , \
174
+ ownerName, ownerPID, [wnumber integerValue ], name);
175
+
176
+ }
177
+ else
178
+ {
179
+ // User didn't say so we grab the first (top most) window
180
+ if ( pidArg == 0 && widArg == 0 )
181
+ {
182
+ grabWindow ([wnumber integerValue ], fileArg);
183
+ break ;
184
+ }
185
+
186
+ // if PID given we grab the first window of the app
187
+ if ( ownerPID == pidArg && widArg == 0 )
188
+ {
189
+ grabWindow ([wnumber integerValue ], fileArg);
190
+ break ;
191
+ }
192
+
193
+ // Finally if they gave a specific window id
194
+ if ( widArg == [wnumber integerValue ] )
195
+ {
196
+ grabWindow ([wnumber integerValue ], fileArg);
197
+ break ;
198
+ }
199
+
200
+ }
201
+ }
202
+
203
+ [pool drain ];
204
+ return 0 ;
205
+ }
0 commit comments