Recent

Author Topic: Detect if the Fn key has been pressed  (Read 1733 times)

Adriaan van Os

  • Newbie
  • Posts: 4
Detect if the Fn key has been pressed
« on: October 22, 2024, 11:05:27 am »
There is no Fn modifier in TShiftStateEnum
  TShiftStateEnum = (ssShift, ssAlt, ssCtrl,
    ssLeft, ssRight, ssMiddle, ssDouble,
    ssMeta, ssSuper, ssHyper, ssAltGr, ssCaps, ssNum,
    ssScroll,ssTriple,ssQuad,ssExtra1,ssExtra2);

This has been discussed before https://forum.lazarus.freepascal.org/index.php/topic,50245.msg366277.html#msg366277

The reply there was that this is handled by the hardware, the BIOS or whatever, modifying the scancode. I doubt that this is true on MacOS, as NSEvent.inc defines

const
  NSAlphaShiftKeyMask = 1 shl 16;
  NSShiftKeyMask = 1 shl 17;
  NSControlKeyMask = 1 shl 18;
  NSAlternateKeyMask = 1 shl 19;
  NSCommandKeyMask = 1 shl 20;
  NSNumericPadKeyMask = 1 shl 21;
  NSHelpKeyMask = 1 shl 22;
  NSFunctionKeyMask = 1 shl 23;
  NSDeviceIndependentModifierFlagsMask = $ffff0000;

So, there actually is a modifier NSFunctionKeyMask on MacOS. And the standard View menu item Enter/Exit Full Screen has a keyboard equivalent that is either Control-Command-F or Fn-F.

If it is Fn-F, it needs to be handled in the software. As a proof, here is the NSEvent when pressing Fn-F

(lldb) po event
NSEvent: type=KeyDown loc=(99.2148,494.188) time=10836.7 flags=0x800100 win=0x11e620bb0 winNum=602 ctxt=0x0 chars="f" unmodchars="f" repeat=0 keyCode=3

where flags does contain the NSFunctionKeyMask modifier.

Regards,

Adriaan van Os

Thaddy

  • Hero Member
  • *****
  • Posts: 16367
  • Censorship about opinions does not belong here.
Re: Detect if the Fn key has been pressed
« Reply #1 on: October 22, 2024, 03:15:36 pm »
It works and it is one of ssMeta, ssSuper, ssHyper. No working mac here so simply try them.
There is nothing wrong with being blunt. At a minimum it is also honest.

Adriaan van Os

  • Newbie
  • Posts: 4
Re: Detect if the Fn key has been pressed
« Reply #2 on: October 22, 2024, 03:53:20 pm »
In other words, it was hacked into another modifier, but we don't know which one ?

Good software starts with clear names for identifiers and constants. So, that requires a constant ssFn = ....one of the others ...;

Regards,

Adriaan van Os


« Last Edit: October 22, 2024, 03:58:15 pm by Adriaan van Os »

Bart

  • Hero Member
  • *****
  • Posts: 5497
    • Bart en Mariska's Webstek
Re: Detect if the Fn key has been pressed
« Reply #3 on: October 22, 2024, 07:37:50 pm »
It works and it is one of ssMeta, ssSuper, ssHyper. No working mac here so simply try them.

I don't think so...
At leats Google and Wikipedia suggest otherwise.

Bart

Thaddy

  • Hero Member
  • *****
  • Posts: 16367
  • Censorship about opinions does not belong here.
Re: Detect if the Fn key has been pressed
« Reply #4 on: October 23, 2024, 10:02:36 am »
Try this Bart, works also for the any key..
Code: Pascal  [Select][+][-]
  1. program handlectrl;
  2. {
  3. msdn ctrl handler with hidden window example transation
  4.  
  5. if the gdi32.dll or user32.dll library are loaded,
  6. SetConsoleCtrlHandler does not get called for the CTRL_LOGOFF_EVENT
  7. and CTRL_SHUTDOWN_EVENT events. The workaround specified is to
  8. create a hidden window, if no window already exists, by calling the
  9. CreateWindowEx method with the dwExStyle parameter set to 0 and listen
  10. for the WM_QUERYENDSESSION and WM_ENDSESSION window messages.
  11. If a window already exists, add the two messages to the existing Window
  12. Procedure.
  13. }
  14. {$mode delphi}{$H+}
  15. {$modeswitch functionreferences}
  16. {$modeswitch anonymousfunctions}
  17.  
  18. uses
  19.   Windows;
  20.  
  21. function  CtrlHandler(fdwCtrlType:dword):Boolean;winapi;
  22. begin
  23.   case fdwCtrlType of
  24.   // Handle the CTRL-C signal.
  25.   CTRL_C_EVENT:
  26.      begin
  27.         writeln('Ctrl-C event');
  28.         Beep(750, 300);
  29.         result := true;
  30.       end;
  31.  
  32.   // CTRL-CLOSE: confirm that the user wants to exit.
  33.   CTRL_CLOSE_EVENT:
  34.     begin
  35.       Beep(600, 200);
  36.       writeln('Ctrl-Close event');
  37.       result := true;
  38.     end;
  39.    
  40.   // Pass other signals to the next handler.
  41.   CTRL_BREAK_EVENT:
  42.     begin
  43.       Beep(900, 200);
  44.       writeln('Ctrl-Break event');
  45.       result := false;
  46.     end;
  47.   CTRL_LOGOFF_EVENT:
  48.     begin
  49.       Beep(1000, 200);
  50.       writeln('Ctrl-Logoff event');
  51.       result := false;
  52.     end;
  53.   CTRL_SHUTDOWN_EVENT:
  54.     begin
  55.       Beep(750, 500);
  56.       writeln('Ctrl-Shutdown event');
  57.       result := false;
  58.     end;
  59.   else
  60.     result := false;
  61.   end;
  62. end;
  63.  
  64.  
  65. function WindowProc(aHwnd:HWND;uMsg:Uint;awParam:WPARAM;alParam:LPARAM):Boolean;winapi;
  66. begin
  67.   case uMsg of
  68.   WM_QUERYENDSESSION:
  69.     begin
  70.       // Check `lParam` for which system shutdown function and handle events.
  71.       // See https://learn.microsoft.com/windows/win32/shutdown/wm-queryendsession
  72.       writeln('Gonna close the app');
  73.       result := TRUE; // Respect user's intent and allow shutdown.
  74.     end;
  75.   WM_ENDSESSION:
  76.     begin
  77.       // Check `lParam` for which system shutdown function and handle events.
  78.       // See https://learn.microsoft.com/windows/win32/shutdown/wm-endsession
  79.       result := false; // We have handled this message.
  80.       exit;
  81.     end;
  82.   WM_KEYUP:
  83.     begin
  84.       MessageBox(0, 'Hello world!', 'sometext', MB_OK or MB_USERICON);
  85.       Result :=true;
  86.      
  87.     end;
  88.   end;
  89.   //else
  90.     result := DefWindowProc(aHwnd, uMsg, awParam, alParam) <> 0;
  91.   end;
  92. //end;
  93.  
  94. var
  95.   wc:WNDCLASS;
  96.   cl:ATOM;
  97.   aHwnd:HWND;
  98.   msg:TMsg;
  99. begin
  100.   wc.lpszClassName := 'HiddenWindowClass';
  101.   wc.lpfnWndProc := @WindowProc;
  102.   cl := registerclass(wc);
  103.   if cl = 0 then
  104.   begin
  105.     writeln('Could not register class');
  106.     Halt(2);
  107.   end;
  108.   writeln('Class registered');
  109.   aHwnd := CreateWindowEx(
  110.         0,
  111.         wc.lpszClassName,
  112.         'Console Control Handler Sample',
  113.         0,
  114.         CW_USEDEFAULT,
  115.         CW_USEDEFAULT,
  116.         CW_USEDEFAULT,
  117.         CW_USEDEFAULT,
  118.         0,
  119.         0,
  120.         0,
  121.         nil
  122.     );
  123.  
  124.   if aHwnd = 0 then
  125.   begin
  126.     writeln('ERROR: Could not create window');
  127.     halt(3);
  128.   end;
  129.   ShowWindow(aHwnd, SW_HIDE);
  130.   if SetConsoleCtrlHandler(@CtrlHandler, TRUE) then
  131.   begin
  132.     writeln('The Control Handler is installed.');
  133.     writeln(' -- Now try pressing Ctrl+C or Ctrl+Break, or');
  134.     writeln('    try logging off or closing the console...');
  135.     writeln('(...waiting in a loop for events...)');
  136.     // Pump message loop for the window we created.
  137.     while PtrInt(GetMessage(msg, 0, 0, 0)) > 0 do
  138.     begin  
  139.       TranslateMessage(msg);
  140.       DispatchMessage(msg);
  141.     end;
  142.   end else
  143.   begin
  144.     writeln('ERROR: Could not set control handler');
  145.     halt(1);
  146.  end;
  147. (*
  148.   if SetConsoleCtrlHandler(@CtrlHandler, true) then
  149.   begin
  150.     writeln('The Control Handler is installed.');
  151.     writeln(' -- Now try pressing Ctrl+C or Ctrl+Break, or');
  152.     writeln('    try logging off or closing the console...');
  153.     writeln('(...waiting in a loop for events...)');
  154.     while 1=1 do
  155.     begin
  156.       sleep(0);
  157.     end;
  158.   end else
  159.   begin
  160.     writeln('ERROR: Could not set control handler');
  161.     Halt(1);
  162.   end;
  163.   *)
  164.   UnRegisterClass(wc.lpszClassName,0);
  165. end.
Global key handlers can also be installed on Linux, so if you insist I can prove you even more wrong.
There is a slight provision for fn in that it depends on the hardware, though the above code also can show, with a slight modification, what the scan code is.
« Last Edit: October 23, 2024, 10:16:45 am by Thaddy »
There is nothing wrong with being blunt. At a minimum it is also honest.

Thaddy

  • Hero Member
  • *****
  • Posts: 16367
  • Censorship about opinions does not belong here.
Re: Detect if the Fn key has been pressed
« Reply #5 on: October 23, 2024, 11:17:42 am »
On liinux, install libevdev-dev for similar fiuctionality.
There is nothing wrong with being blunt. At a minimum it is also honest.

Thaddy

  • Hero Member
  • *****
  • Posts: 16367
  • Censorship about opinions does not belong here.
Re: Detect if the Fn key has been pressed
« Reply #6 on: October 23, 2024, 11:28:05 am »
Forget the above. It is too hardware specific. Discovered after changing between laptops and th code for fn not working there.
There is nothing wrong with being blunt. At a minimum it is also honest.

Adriaan van Os

  • Newbie
  • Posts: 4
Re: Detect if the Fn key has been pressed
« Reply #7 on: October 23, 2024, 04:57:46 pm »
I found zero occurrences of NSFunctionKeyMask in the source code of Lazarus 3.6 (except for the Cocoa headers and the fpc ptc package). If this is not correct, please point me to the right line of code.

Regards,

Adriaan van Os

TRon

  • Hero Member
  • *****
  • Posts: 3787
Re: Detect if the Fn key has been pressed
« Reply #8 on: October 23, 2024, 05:05:57 pm »
@Adriaan van Os
I do not know anything about MacOS but why is the documentation stating NSFunctionKeyMask is deprecated ?

There would be no benefit to add support for deprecated functionality so you might have to figure this one out yourself.

Also, I am only able to read something about the numeric keypad in that doc....
I do not have to remember anything anymore thanks to total-recall.

Thaddy

  • Hero Member
  • *****
  • Posts: 16367
  • Censorship about opinions does not belong here.
Re: Detect if the Fn key has been pressed
« Reply #9 on: October 23, 2024, 05:54:21 pm »
According to a close friend and Mac user, but a C programmer at least one of those should work;
ssMeta, ssSuper, ssHyper,ssTriple,ssQuad,ssExtra1,ssExtra2);
on Apple hardware, but I can not test it anymore.
Maybe ask Jonas?
There is nothing wrong with being blunt. At a minimum it is also honest.

carl_caulkett

  • Hero Member
  • *****
  • Posts: 649
Re: Detect if the Fn key has been pressed
« Reply #10 on: October 23, 2024, 06:47:45 pm »
According to a close friend and Mac user, but a C programmer at least one of those should work;
ssMeta, ssSuper, ssHyper,ssTriple,ssQuad,ssExtra1,ssExtra2);
on Apple hardware, but I can not test it anymore.
Maybe ask Jonas?

I can confirm that the ssMeta key, in combination with other keys works with Linux. I'm putting together a cross-platform app in Lazarus and I have the Cmd+Comma combination working to bring up a settings page, and it works in macOS and Ubuntu Linux. 
"It builds... ship it!"

dsiders

  • Hero Member
  • *****
  • Posts: 1324
Re: Detect if the Fn key has been pressed
« Reply #11 on: October 23, 2024, 09:25:19 pm »
According to stack overflow (and other) answers, the Fn key does not generate a scan code. It is handled by the low-level keyboard drivers on platforms where it is implemented. Windows uses the Registry (of course it does).

Preview the next Lazarus documentation release at: https://dsiders.gitlab.io/lazdocsnext

MarkMLl

  • Hero Member
  • *****
  • Posts: 8104
Re: Detect if the Fn key has been pressed
« Reply #12 on: October 23, 2024, 11:58:38 pm »
This ain't my fight, but I'd suggest that the real issue is what the LCL does with that key.

From having looked at keyboards in the context of trying to wring every possible combination out of the system to implement an APL terminal, I'd suggest that it's what the widget set reports that's important: and I've certainly seen different behaviour between gtk2 and Qt particularly when comparing IBM and Sun keyboards.

It should not be necessary to consult a lower-level API, the use of which could potentially interfere with the operation of the widget set.

If the LCL interprets different widget sets differently it's potentially a bug, or at least the subject of a feature request once the current and intended behaviours have been fully defined.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

dsiders

  • Hero Member
  • *****
  • Posts: 1324
Re: Detect if the Fn key has been pressed
« Reply #13 on: October 24, 2024, 12:21:09 am »
This ain't my fight, but I'd suggest that the real issue is what the LCL does with that key.

<Shrug>. If Fn doesn't generate a distinct scan code, there is nothing LCL can do with it... as a virtual key code or as a shift modifier. Just my .02USD.
Preview the next Lazarus documentation release at: https://dsiders.gitlab.io/lazdocsnext

Thaddy

  • Hero Member
  • *****
  • Posts: 16367
  • Censorship about opinions does not belong here.
Re: Detect if the Fn key has been pressed
« Reply #14 on: October 24, 2024, 06:32:35 am »
Well, it does generate a code, but it is not distinct across machines/brands/bios. Which makes it unusable.
(you can make it work on your computer, though)
What I discovered yesterday is that it is detectable - of course, otherwise it would not "function" - but completely useless.
« Last Edit: October 24, 2024, 06:41:52 am by Thaddy »
There is nothing wrong with being blunt. At a minimum it is also honest.

 

TinyPortal © 2005-2018