Blender  V3.3
GHOST_WindowViewCocoa.h
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
3 
4 /* The Carbon API is still needed to check if the Input Source (Input Method or IME) is valid. */
5 #ifdef WITH_INPUT_IME
6 # import <Carbon/Carbon.h>
7 #endif
8 
9 /* NSView subclass for drawing and handling input.
10  *
11  * COCOA_VIEW_BASE_CLASS will be either NSView or NSOpenGLView depending if
12  * we use a Metal or OpenGL layer for drawing in the view. We use macros
13  * to defined classes for each case, so we don't have to duplicate code as
14  * Objective-C does not have multiple inheritance. */
15 
16 // We need to subclass it in order to give Cocoa the feeling key events are trapped
17 @interface COCOA_VIEW_CLASS : COCOA_VIEW_BASE_CLASS <NSTextInputClient>
18 {
21 
22  bool composing;
23  NSString *composing_text;
24 
25 #ifdef WITH_INPUT_IME
26  struct {
27  GHOST_ImeStateFlagCocoa state_flag;
28  NSRect candidate_window_position;
29 
30  /* Event data. */
31  GHOST_TEventImeData event;
32  std::string result;
33  std::string composite;
34  std::string combined_result;
35  } ime;
36 #endif
37 }
38 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa
39  windowCocoa:(GHOST_WindowCocoa *)winCocoa;
40 
41 #ifdef WITH_INPUT_IME
42 - (void)beginIME:(int32_t)x y:(int32_t)y w:(int32_t)w h:(int32_t)h completed:(bool)completed;
43 
44 - (void)endIME;
45 #endif
46 @end
47 
48 @implementation COCOA_VIEW_CLASS
49 
50 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa
51  windowCocoa:(GHOST_WindowCocoa *)winCocoa
52 {
53  systemCocoa = sysCocoa;
54  associatedWindow = winCocoa;
55 
56  composing = false;
57  composing_text = nil;
58 
59 #ifdef WITH_INPUT_IME
60  ime.state_flag = 0;
61  ime.candidate_window_position = NSZeroRect;
62  ime.event.cursor_position = -1;
63  ime.event.target_start = -1;
64  ime.event.target_end = -1;
65 
66  /* Register a function to be executed when Input Method is changed using
67  * 'Control + Space' or language-specific keys (such as 'Eisu / Kana' key for Japanese). */
68  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
69  [center addObserver:self
70  selector:@selector(ImeDidChangeCallback:)
71  name:NSTextInputContextKeyboardSelectionDidChangeNotification
72  object:nil];
73 #endif
74 }
75 
76 - (BOOL)acceptsFirstResponder
77 {
78  return YES;
79 }
80 
81 - (BOOL)acceptsFirstMouse:(NSEvent *)event
82 {
83  return YES;
84 }
85 
86 // The trick to prevent Cocoa from complaining (beeping)
87 - (void)keyDown:(NSEvent *)event
88 {
89 #ifdef WITH_INPUT_IME
90  [self checkKeyCodeIsControlChar:event];
91  const bool ime_process = [self isProcessedByIme];
92 #else
93  const bool ime_process = false;
94 #endif
95 
96  if (!ime_process) {
98  }
99 
100  /* Start or continue composing? */
101  if ([[event characters] length] == 0 || [[event charactersIgnoringModifiers] length] == 0 ||
102  composing || ime_process) {
103  composing = YES;
104 
105  // interpret event to call insertText
106  [self interpretKeyEvents:[NSArray arrayWithObject:event]]; // calls insertText
107 
108 #ifdef WITH_INPUT_IME
109  // For Korean input, control characters are also processed by handleKeyEvent.
110  const int controlCharForKorean = (GHOST_IME_COMPOSITION_EVENT | GHOST_IME_RESULT_EVENT |
111  GHOST_IME_KEY_CONTROL_CHAR);
112  if (((ime.state_flag & controlCharForKorean) == controlCharForKorean)) {
113  systemCocoa->handleKeyEvent(event);
114  }
115 
116  ime.state_flag &= ~(GHOST_IME_COMPOSITION_EVENT | GHOST_IME_RESULT_EVENT);
117 
118  ime.combined_result.clear();
119 #endif
120 
121  return;
122  }
123 }
124 
125 - (void)keyUp:(NSEvent *)event
126 {
127  systemCocoa->handleKeyEvent(event);
128 }
129 
130 - (void)flagsChanged:(NSEvent *)event
131 {
132  systemCocoa->handleKeyEvent(event);
133 }
134 
135 - (void)mouseDown:(NSEvent *)event
136 {
138 }
139 
140 - (void)mouseUp:(NSEvent *)event
141 {
143 }
144 
145 - (void)rightMouseDown:(NSEvent *)event
146 {
148 }
149 
150 - (void)rightMouseUp:(NSEvent *)event
151 {
153 }
154 
155 - (void)mouseMoved:(NSEvent *)event
156 {
158 }
159 
160 - (void)mouseDragged:(NSEvent *)event
161 {
163 }
164 
165 - (void)rightMouseDragged:(NSEvent *)event
166 {
168 }
169 
170 - (void)scrollWheel:(NSEvent *)event
171 {
173 }
174 
175 - (void)otherMouseDown:(NSEvent *)event
176 {
178 }
179 
180 - (void)otherMouseUp:(NSEvent *)event
181 {
183 }
184 
185 - (void)otherMouseDragged:(NSEvent *)event
186 {
188 }
189 
190 - (void)magnifyWithEvent:(NSEvent *)event
191 {
193 }
194 
195 - (void)smartMagnifyWithEvent:(NSEvent *)event
196 {
198 }
199 
200 - (void)rotateWithEvent:(NSEvent *)event
201 {
203 }
204 
205 - (void)tabletPoint:(NSEvent *)event
206 {
207  systemCocoa->handleTabletEvent(event, [event type]);
208 }
209 
210 - (void)tabletProximity:(NSEvent *)event
211 {
212  systemCocoa->handleTabletEvent(event, [event type]);
213 }
214 
215 - (BOOL)isOpaque
216 {
217  return YES;
218 }
219 
220 - (void)drawRect:(NSRect)rect
221 {
222  if ([self inLiveResize]) {
223  /* Don't redraw while in live resize */
224  }
225  else {
226  [super drawRect:rect];
228 
229  /* For some cases like entering fullscreen we need to redraw immediately
230  * so our window does not show blank during the animation */
233  }
234 }
235 
236 // Text input
237 
238 - (void)composing_free
239 {
240  composing = NO;
241 
242  if (composing_text) {
243  [composing_text release];
244  composing_text = nil;
245  }
246 }
247 
248 // Processes the Result String sent from the Input Method.
249 - (void)insertText:(id)chars replacementRange:(NSRange)replacementRange
250 {
251  [self composing_free];
252 
253 #ifdef WITH_INPUT_IME
254  if (ime.state_flag & GHOST_IME_ENABLED) {
255  if (!(ime.state_flag & GHOST_IME_COMPOSING)) {
256  [self processImeEvent:GHOST_kEventImeCompositionStart];
257  }
258 
259  // For Chinese and Korean input, insertText may be executed twice with a single keyDown.
260  if (ime.state_flag & GHOST_IME_RESULT_EVENT) {
261  ime.combined_result += [self convertNSString:chars];
262  }
263  else {
264  ime.combined_result = [self convertNSString:chars];
265  }
266 
267  [self setImeResult:ime.combined_result];
268 
269  /* For Korean input, both "Result Event" and "Composition Event"
270  * can occur in a single keyDown. */
271  if (![self ime_did_composition]) {
272  [self processImeEvent:GHOST_kEventImeComposition];
273  }
274  ime.state_flag |= GHOST_IME_RESULT_EVENT;
275 
276  [self processImeEvent:GHOST_kEventImeCompositionEnd];
277  ime.state_flag &= ~GHOST_IME_COMPOSING;
278  }
279 #endif
280 }
281 
282 // Processes the Composition String sent from the Input Method.
283 - (void)setMarkedText:(id)chars
284  selectedRange:(NSRange)range
285  replacementRange:(NSRange)replacementRange
286 {
287  [self composing_free];
288 
289  if ([chars length] == 0) {
290 #ifdef WITH_INPUT_IME
291  // Processes when the last Composition String is deleted.
292  if (ime.state_flag & GHOST_IME_COMPOSING) {
293  [self setImeResult:std::string()];
294  [self processImeEvent:GHOST_kEventImeComposition];
295  [self processImeEvent:GHOST_kEventImeCompositionEnd];
296  ime.state_flag &= ~GHOST_IME_COMPOSING;
297  }
298 #endif
299 
300  return;
301  }
302 
303  // start composing
304  composing = YES;
305  composing_text = [chars copy];
306 
307  // chars of markedText by Input Method is an instance of NSAttributedString
308  if ([chars isKindOfClass:[NSAttributedString class]]) {
309  composing_text = [[chars string] copy];
310  }
311 
312  // if empty, cancel
313  if ([composing_text length] == 0)
314  [self composing_free];
315 
316 #ifdef WITH_INPUT_IME
317  if (ime.state_flag & GHOST_IME_ENABLED) {
318  if (!(ime.state_flag & GHOST_IME_COMPOSING)) {
319  ime.state_flag |= GHOST_IME_COMPOSING;
320  [self processImeEvent:GHOST_kEventImeCompositionStart];
321  }
322 
323  [self setImeComposition:composing_text selectedRange:range];
324 
325  // For Korean input, setMarkedText may be executed twice with a single keyDown.
326  if (![self ime_did_composition]) {
327  ime.state_flag |= GHOST_IME_COMPOSITION_EVENT;
328  [self processImeEvent:GHOST_kEventImeComposition];
329  }
330  }
331 #endif
332 }
333 
334 - (void)unmarkText
335 {
336  [self composing_free];
337 }
338 
339 - (BOOL)hasMarkedText
340 {
341  return (composing) ? YES : NO;
342 }
343 
344 - (void)doCommandBySelector:(SEL)selector
345 {
346 }
347 
348 - (BOOL)isComposing
349 {
350  return composing;
351 }
352 
353 - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range
354  actualRange:(NSRangePointer)actualRange
355 {
356  return [[[NSAttributedString alloc] init] autorelease];
357 }
358 
359 - (NSRange)markedRange
360 {
361  unsigned int length = (composing_text) ? [composing_text length] : 0;
362 
363  if (composing)
364  return NSMakeRange(0, length);
365 
366  return NSMakeRange(NSNotFound, 0);
367 }
368 
369 - (NSRange)selectedRange
370 {
371  unsigned int length = (composing_text) ? [composing_text length] : 0;
372  return NSMakeRange(0, length);
373 }
374 
375 // Specify the position where the Chinese and Japanese candidate windows are displayed.
376 - (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange
377 {
378 #ifdef WITH_INPUT_IME
379  if (ime.state_flag & GHOST_IME_ENABLED) {
380  return ime.candidate_window_position;
381  }
382 #endif
383  return NSZeroRect;
384 }
385 
386 - (NSUInteger)characterIndexForPoint:(NSPoint)point
387 {
388  return NSNotFound;
389 }
390 
391 - (NSArray *)validAttributesForMarkedText
392 {
393  return [NSArray array];
394 }
395 
396 #ifdef WITH_INPUT_IME
397 - (void)checkImeEnabled
398 {
399  ime.state_flag &= ~GHOST_IME_ENABLED;
400 
401  if (ime.state_flag & GHOST_IME_INPUT_FOCUSED) {
402  /* Since there are no functions in Cocoa API,
403  * we will use the functions in the Carbon API. */
404  TISInputSourceRef currentKeyboardInputSource = TISCopyCurrentKeyboardInputSource();
405  bool ime_enabled = !CFBooleanGetValue((CFBooleanRef)TISGetInputSourceProperty(
406  currentKeyboardInputSource, kTISPropertyInputSourceIsASCIICapable));
407  CFRelease(currentKeyboardInputSource);
408 
409  if (ime_enabled) {
410  ime.state_flag |= GHOST_IME_ENABLED;
411  return;
412  }
413  }
414  return;
415 }
416 
417 - (void)ImeDidChangeCallback:(NSNotification *)notification
418 {
419  [self checkImeEnabled];
420 }
421 
422 - (void)setImeCandidateWinPos:(int32_t)x y:(int32_t)y w:(int32_t)w h:(int32_t)h
423 {
424  int32_t outX, outY;
425  associatedWindow->clientToScreen(x, y, outX, outY);
426  ime.candidate_window_position = NSMakeRect((CGFloat)outX, (CGFloat)outY, (CGFloat)w, (CGFloat)h);
427 }
428 
429 - (void)beginIME:(int32_t)x y:(int32_t)y w:(int32_t)w h:(int32_t)h completed:(bool)completed
430 {
431  ime.state_flag |= GHOST_IME_INPUT_FOCUSED;
432  [self checkImeEnabled];
433  [self setImeCandidateWinPos:x y:y w:w h:h];
434 }
435 
436 - (void)endIME
437 {
438  ime.state_flag = 0;
439  ime.result.clear();
440  ime.composite.clear();
441 
442  [self unmarkText];
443  [[NSTextInputContext currentInputContext] discardMarkedText];
444 }
445 
446 - (void)processImeEvent:(GHOST_TEventType)imeEventType
447 {
448  ime.event.result_len = (GHOST_TUserDataPtr)ime.result.size();
449  ime.event.result = (GHOST_TUserDataPtr)ime.result.c_str();
450  ime.event.composite_len = (GHOST_TUserDataPtr)ime.composite.size();
451  ime.event.composite = (GHOST_TUserDataPtr)ime.composite.c_str();
452 
453  GHOST_Event *event = new GHOST_EventIME(
454  systemCocoa->getMilliSeconds(), imeEventType, associatedWindow, &ime.event);
455  systemCocoa->pushEvent(event);
456 }
457 
458 - (std::string)convertNSString:(NSString *)inString
459 {
460  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
461  std::string str([inString UTF8String]);
462  [pool drain];
463  return str;
464 }
465 
466 - (void)setImeComposition:(NSString *)inString selectedRange:(NSRange)range
467 {
468  ime.composite = [self convertNSString:inString];
469 
470  // For Korean input, both "Result Event" and "Composition Event" can occur in a single keyDown.
471  if (!(ime.state_flag & GHOST_IME_RESULT_EVENT)) {
472  ime.result.clear();
473  }
474 
475  /* The target string is equivalent to the string in selectedRange of setMarkedText.
476  * The cursor is displayed at the beginning of the target string. */
477  char *front_string = (char *)[[inString substringWithRange:NSMakeRange(0, range.location)]
478  UTF8String];
479  char *selected_string = (char *)[[inString substringWithRange:range] UTF8String];
480  ime.event.cursor_position = strlen(front_string);
481  ime.event.target_start = ime.event.cursor_position;
482  ime.event.target_end = ime.event.target_start + strlen(selected_string);
483 }
484 
485 - (void)setImeResult:(std::string)result
486 {
487  ime.result = result;
488  ime.composite.clear();
489  ime.event.cursor_position = -1;
490  ime.event.target_start = -1;
491  ime.event.target_end = -1;
492 }
493 
494 - (void)checkKeyCodeIsControlChar:(NSEvent *)event
495 {
496  ime.state_flag &= ~GHOST_IME_KEY_CONTROL_CHAR;
497 
498  /* Don't use IME for command and ctrl key combinations, these are shortcuts. */
499  if ([event modifierFlags] & (NSEventModifierFlagCommand | NSEventModifierFlagControl)) {
500  ime.state_flag |= GHOST_IME_KEY_CONTROL_CHAR;
501  return;
502  }
503 
504  /* Don't use IME for these control keys. */
505  switch ([event keyCode]) {
506  case kVK_ANSI_KeypadEnter:
507  case kVK_ANSI_KeypadClear:
508  case kVK_F1:
509  case kVK_F2:
510  case kVK_F3:
511  case kVK_F4:
512  case kVK_F5:
513  case kVK_F6:
514  case kVK_F7:
515  case kVK_F8:
516  case kVK_F9:
517  case kVK_F10:
518  case kVK_F11:
519  case kVK_F12:
520  case kVK_F13:
521  case kVK_F14:
522  case kVK_F15:
523  case kVK_F16:
524  case kVK_F17:
525  case kVK_F18:
526  case kVK_F19:
527  case kVK_F20:
528  case kVK_UpArrow:
529  case kVK_DownArrow:
530  case kVK_LeftArrow:
531  case kVK_RightArrow:
532  case kVK_Return:
533  case kVK_Delete:
534  case kVK_ForwardDelete:
535  case kVK_Escape:
536  case kVK_Tab:
537  case kVK_Home:
538  case kVK_End:
539  case kVK_PageUp:
540  case kVK_PageDown:
541  case kVK_VolumeUp:
542  case kVK_VolumeDown:
543  case kVK_Mute:
544  ime.state_flag |= GHOST_IME_KEY_CONTROL_CHAR;
545  return;
546  }
547 }
548 
549 - (bool)ime_did_composition
550 {
551  return (ime.state_flag & GHOST_IME_COMPOSITION_EVENT) ||
552  (ime.state_flag & GHOST_IME_RESULT_EVENT);
553 }
554 
555 /* Even if IME is enabled, when not composing, control characters
556  * (such as arrow, enter, delete) are handled by handleKeyEvent. */
557 - (bool)isProcessedByIme
558 {
559  return (
560  (ime.state_flag & GHOST_IME_ENABLED) &&
561  ((ime.state_flag & GHOST_IME_COMPOSING) || !(ime.state_flag & GHOST_IME_KEY_CONTROL_CHAR)));
562 }
563 #endif /* WITH_INPUT_IME */
564 
565 @end
NSNotificationCenter * center
void * GHOST_TUserDataPtr
Definition: GHOST_Types.h:72
GHOST_TEventType
Definition: GHOST_Types.h:169
@ GHOST_kEventWindowUpdate
Definition: GHOST_Types.h:192
#define COCOA_VIEW_BASE_CLASS
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
PyObject * self
Definition: bpy_driver.c:165
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition: btQuadWord.h:119
GHOST_TSuccess handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa *window)
GHOST_TSuccess handleMouseEvent(void *eventPtr)
GHOST_TSuccess handleKeyEvent(void *eventPtr)
GHOST_TSuccess handleTabletEvent(void *eventPtr, short eventType)
uint64_t getMilliSeconds() const
void dispatchEvents()
GHOST_TSuccess pushEvent(GHOST_IEvent *event)
bool getImmediateDraw(void) const
void clientToScreen(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const
SyclQueue void void size_t num_bytes void
#define str(s)
GHOST_SystemCocoa * systemCocoa
GHOST_WindowCocoa * associatedWindow
T length(const vec_base< T, Size > &a)
signed int int32_t
Definition: stdint.h:77