-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrotator.mm
executable file
·172 lines (151 loc) · 6.66 KB
/
rotator.mm
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
#import <AppKit/AppKit.h>
#import <QuartzCore/QuartzCore.h>
#include "rotator.h"
const int DIRECTION_LEFT = 0;
napi_value rotate(napi_env env, napi_callback_info info) {
NSLog(@"rotate()");
napi_status status;
size_t argc = 4;
napi_value args[4];
status = napi_get_cb_info(env, info, &argc, args, 0, 0);
if (status != napi_ok) {
napi_throw_error(env, NULL, "rotate(): failed to get arguments");
return NULL;
} else if (argc < 4) {
napi_throw_error(env, NULL, "rotate(): wrong number of arguments");
return NULL;
}
void *windowBuffer;
size_t windowBufferLength;
status =
napi_get_buffer_info(env, args[0], &windowBuffer, &windowBufferLength);
if (status != napi_ok) {
napi_throw_error(env, NULL, "rotate(): cannot read window handle");
return NULL;
} else if (windowBufferLength == 0) {
napi_throw_error(env, NULL, "rotate(): empty window handle");
return NULL;
}
NSView *mainWindowView = *static_cast<NSView **>(windowBuffer);
if (![mainWindowView respondsToSelector:@selector(window)] ||
mainWindowView.window == nil) {
napi_throw_error(env, NULL, "rotate(): NSView doesn't contain window");
return NULL;
}
void *electronScreenshotBuffer;
size_t electronScreenshotBufferLength;
status = napi_get_buffer_info(env, args[1], &electronScreenshotBuffer,
&electronScreenshotBufferLength);
if (status != napi_ok) {
napi_throw_error(env, NULL, "rotate(): cannot read screenshot handle");
return NULL;
} else if (electronScreenshotBufferLength == 0) {
napi_throw_error(env, NULL, "rotate(): empty screenshot handle");
return NULL;
}
int duration;
status = napi_get_value_int32(env, args[2], &duration);
if (status != napi_ok) {
napi_throw_error(env, NULL, "rotate(): cannot read duration from args");
return NULL;
} else if (duration == 0) {
napi_throw_error(env, NULL, "rotate(): empty duration arg");
return NULL;
}
int direction;
status = napi_get_value_int32(env, args[3], &direction);
if (status != napi_ok) {
napi_throw_error(env, NULL, "rotate(): cannot read direction from args");
return NULL;
}
NSWindow *window = mainWindowView.window;
NSView *windowView = [window.contentView superview];
CGFloat shadowMaxLength = 40.0;
CGFloat width = NSWidth(window.frame);
CGFloat height = NSHeight(window.frame);
CGFloat l = sqrt(width * width + height * height) + shadowMaxLength * 2;
CGFloat offsetX = (l - width) / 2;
CGFloat offsetY = (l - height) / 2;
// create bigger window to play rotation animation in it
NSRect animationWindowContentRect = NSMakeRect(
window.frame.origin.x - offsetX, window.frame.origin.y - offsetY, l, l);
NSWindow *animationWindow =
[[NSWindow alloc] initWithContentRect:animationWindowContentRect
styleMask:NSWindowStyleMaskBorderless
backing:NSBackingStoreBuffered
defer:NO];
[animationWindow setOpaque:NO];
[animationWindow setHasShadow:NO];
[animationWindow setBackgroundColor:[NSColor clearColor]];
[animationWindow.contentView setWantsLayer:YES];
// create native window frame screenshot (doesn't include electron's webview)
NSBitmapImageRep *windowScreenshotRep =
[windowView bitmapImageRepForCachingDisplayInRect:windowView.bounds];
[windowView cacheDisplayInRect:windowView.bounds
toBitmapImageRep:windowScreenshotRep];
NSSize windowScreenshotSize =
NSMakeSize(CGImageGetWidth([windowScreenshotRep CGImage]),
CGImageGetHeight([windowScreenshotRep CGImage]));
NSImage *windowScreenshot =
[[NSImage alloc] initWithSize:windowScreenshotSize];
[windowScreenshot addRepresentation:windowScreenshotRep];
NSLog(@"rotate(): window screenshot size: %0.0f x "
@"%0.0f",
windowScreenshotSize.width, windowScreenshotSize.height);
// create electron app screenshot
NSData *data = [NSData dataWithBytes:electronScreenshotBuffer
length:electronScreenshotBufferLength];
NSBitmapImageRep *electronScreenshotRep =
[NSBitmapImageRep imageRepWithData:data];
NSSize electronScreenshotSize =
NSMakeSize(CGImageGetWidth([electronScreenshotRep CGImage]),
CGImageGetHeight([electronScreenshotRep CGImage]));
NSImage *electronScreenshot =
[[NSImage alloc] initWithSize:electronScreenshotSize];
[electronScreenshot addRepresentation:electronScreenshotRep];
NSLog(@"rotate(): electron screenshot size: %0.0f x "
@"%0.0f",
electronScreenshotSize.width, electronScreenshotSize.height);
// combine two screenshots -- put electron app screenshot into native one
[windowScreenshot lockFocus];
CGRect electronScreenshotRect = CGRectMake(0, 0, electronScreenshotSize.width,
electronScreenshotSize.height);
[electronScreenshot drawInRect:electronScreenshotRect];
[windowScreenshot unlockFocus];
// create image layer with rounded corners
CALayer *imageLayer = [CALayer layer];
[imageLayer setContents:windowScreenshot];
[imageLayer setCornerRadius:10.0f];
[imageLayer setMasksToBounds:YES]; // cuts shadows as well
// show the animation window to calculate its real offset from the top of the
// screen -- macOS doesn't allow window to go up of the top edge of the screen
[animationWindow setAlphaValue:0.0];
[animationWindow makeKeyAndOrderFront:nil];
offsetY = window.frame.origin.y - animationWindow.frame.origin.y;
[imageLayer setFrame:CGRectMake(offsetX, offsetY, width, height)];
// add image layer to the animation window, set up OS shadows
CALayer *animationWindowLayer = [animationWindow.contentView layer];
[animationWindowLayer addSublayer:imageLayer];
[animationWindowLayer setShadowRadius:20.0f];
[animationWindowLayer setShadowOpacity:0.7f];
[animationWindowLayer setShadowOffset:CGSizeMake(0, -20)];
// replace the original window with its screenshot and start animation
[window setAlphaValue:0.0];
[animationWindow setAlphaValue:1.0];
[CATransaction begin];
CABasicAnimation *animation =
[CABasicAnimation animationWithKeyPath:@"transform.rotation"];
[animation setFromValue:[NSNumber numberWithDouble:0.0]];
CGFloat endAngle = (direction == DIRECTION_LEFT ? 1 : -1) * 2.0 * M_PI;
[animation setToValue:[NSNumber numberWithDouble:endAngle]];
[animation setDuration:duration / 1000.0];
[CATransaction setCompletionBlock:^{
[window setAlphaValue:1.0];
[animationWindow close];
NSLog(@"rotate(): animation done");
}];
[imageLayer addAnimation:animation forKey:@"rotation"];
[CATransaction commit];
NSLog(@"rotate(): done");
return NULL;
}