Key and coded key shift combo (cmd + something)

Hi I am attempting to make a simple sketch that draws two different shapes depending on what keys a user presses. The first part of the code detects when the lowercase ‘o’ is pressed and draws a small ellipse. The second part of the code detects that the user is pressing the shift key. According to the Processing documentation this is handled as a CODED key. The code has a println function that outputs data to the console confirming that a CODED key is pressed. It also outputs that the shift key’s code is 16 and that the uppercase ‘O’'s code is 79. However the larger ellipse is not drawn. Why doesn’t the larger ellipse draw?

int posX, posY;
float diam;

void setup(){
  posX = width/2;
  posY = height/2;
  diam = width*0.9;
  frameRate(10);
}

void draw(){
  background(200);
  
  
  if (keyPressed) {
    println("outer keypressed if -- the key is: "+key);
    println("outer keypressed if -- CODED key is: "+keyCode);
    //FIRST PART - DETECT LOWERCASE o
    if (key == 'o') {
      println("inside key is lowercase o if -- draw small circle");
      //draw small ellipse for lowercase 'o'
      ellipse(posX, posY, diam/2, diam/2);
    }//end key is lowercase o if
    //SECOND PART - DETECT UPPERCASE O
    if (key == CODED) {
      println("inside key is a CODED key if and keyCode is: "+keyCode);
      if (keyCode == 79) {
        println("inside key is o and CODED key is SHIFT -- draw large circle");
        //draw larger ellipse for uppercase 'O'
        ellipse(posX, posY, diam, diam);
      }//end keyCode is 79 if; code 79 is an uppercase O
    }//end key is CODED key if
    
  }//end keypressed if
  

}//end draw function

To detect a upper case ‘O’, you can simply use

if(key == 'O')

You don’t have to do difficult with a coded key.

3 Likes

Shouldn’t you have size(300,300); as first line in setup ()

Hi sterretje, thanks for the help. What is a way to work with coded keys? Do you know how to work with other key combos like cmd + something or alt + something or shift + ctrl + something?

1 Like

First for cursor LEFT and RIGHT you can use keyCode
if I remember correctly

For ESC, Return and the like just key

See reference

Example with Ctrl

To get ctrl-a and so on is not straightforward.

In my opinion you have to check for ctrl in keyPressed and set a boolean ctrlIsDown to true.
(boolean ctrlIsDown=false; before setup())

In the function keyReleased evaluate keyCode and
if it’s ctrl, set your boolean to false.

Now when a is pressed you have to check your
boolean whether we have a or ctrl-a.

For Alt is the same.

For Shift it would maybe also work, but
you can get A instead of a here anyway,
so it doesn’t make much sense.

Maybe ctrl-shift-a is different.
Or it’s just ctrl-A in this case.

Now you can check ctrlIsDown plus key==‘a’
or check ctrlIsDown plus altIsDown plus key==‘a’
or check ctrlIsDown plus altIsDown plus shiftIsDown plus key==‘a’

Chrisir

How you do this in detail depends on what your goal is.

You can use switch (key) for ‘a’ etc.

switch (keyCode) for ctrl etc.

You can assign a int value to an variable command
and evaluate command in a separate function.

For example you can use the ascii value for a,b,c…
Add 1000 for shift, 2000 for shift-ctrl etc.

Below the very basics of what I use.

boolean hasAlt = false;
boolean hasCtrl = false;
boolean hasShift = false;


void setup()
{
}

void draw()
{
}

void keyPressed()
{
  if (key == CODED)
  {
    switch(keyCode)
    {
    case ALT:
      hasAlt = true;
      break;
    case CONTROL:
      hasCtrl = true;
      break;
    case SHIFT:
      hasShift = true;
      break;
    default:
      break;
    }
  } // 
  else
  {
    if (hasAlt == true) println("<alt>" + key);
    else println(key);
  }
}


void keyReleased()
{
  if (key == CODED)
  {
    switch(keyCode)
    {
    case ALT:
      hasAlt = false;
      break;
    case CONTROL:
      hasCtrl = false;
      break;
    case SHIFT:
      hasShift = false;
      break;
    }
  }
}

You need to expand based on your needs.

1 Like

Hi guys, thank you for your time and explanations. I tried making simple sketches with the different approaches you guys suggested. The first attempt works with Chrisir’s first suggestion where boolean variables are combined with switches in the keyPressed and keyReleased functions. This works only for when a + codedKey is pressed. If the inverse is performed (codedKey + a) then it doesn’t work and I still don’t understand why. Here is the code for the first way I tried to build the functionality:

int posX, posY;
float diam;
boolean a, codKeyBool1;

//int codedKey1 = 16; //using this int as the case name gives an error
// Shift          -> 16
// Control      -> 17
// Option & Alt -> 18

void setup(){
  posX = width/2;
  posY = height/2;
  diam = width*0.9;
  codKeyBool1 = false;
  a = false;
}

void draw(){
  background(200);

      //codedKey + a doesn't work; a + codedKey works; why?
   if (codKeyBool1 && a || a && codKeyBool1) {
       arc(posX, posY, diam, diam, 0, PI); 
    }
}

void keyPressed(){
  switch (key) {
   case 'a' :  a = true;
               println("in keyPressed function switch case a: "+a);
               break;
  }
  //if (key == CODED) {//tried including this if to see if codedKey + a would work
     switch (keyCode) {
      case 18 : codKeyBool1 = true; //18 is option on macOS
                println("in keyPressed function switch case for codedKey: "+codKeyBool1);
                break;
     }//end switch
  //}//end if key CODED statement
}//end keyPressed func

void keyReleased(){
  switch (key) {
   case 'a' :  a = false;
               println("in keyReleased function switch case a: "+a);
               break;
  }
  
  switch (keyCode) {
   case 18 : codKeyBool1 = false;
             println("in keyReleased function switch case for codedKey: "+codKeyBool1);
             break;
   //case insert other keys
  }
}//end keyReleased func

For my second attempt I used all of the code sterretje provided and only inserted the following into the draw function.

background(200);
  
  if (keyPressed){
    if (key == 'a'){ //this if works
     fill(0,0,255);
     text("key a is pressed", 10,10);
     arc(width/4, height/4, width/4, height/4, -PI, PI); 
    }
    if (hasCtrl && key == 'a'){ //this if does not work
     fill(0);
     arc(width/2, height/2, width*0.9, width*0.9, 0, PI); 
    }
  }
  
 if (key == CODED){ //this doesn't work also
     if (keyCode == 17 && keyCode == 65){
      text("17 is ctrl && 65 is a", 10, 20); 
     }
    }

  fill(255,0,0,50);
  arc(width/2, height/2, width*0.9, width*0.9, -PI, 0); 
  println("hasCtrl: "+hasCtrl);
  println("keyCode: "+keyCode);

I’m not sure if the problem might be that the key combination creates a new keyCode that accounts for the combo. Output from the println + keyCode says a’s keyCode is 65 but sometimes a 0 is output also. The zero is output when the sketch is launched and when the a + ctrl combo is being pressed.

In your first code version you would need to hold both keys down to let it work.

I suggest to set an arc-flag where you check
‘a’ in keyPressed (only when ctrl-flag is on)

In general you don’t need a flag for a etc., only for ctrl
cmd alt etc.

But when a program requires ctrl-a, it’s always assumed that
you press and hold ctrl and then click a briefly.
Never the other way round.
So, is this the way you test the Sketches?

This is in the code that you showed; please note the italics. The reason is that the forum software thinks that you want to show text in italics when it sees an * . To prevent this from happening, please use code tags (the </> button above the composer window. Thanks

<ctrl> keys are special; I forgot about that. E.g. <ctrl>A is a coded key; the key value is 0x0001 (1 decimal)


boolean hasAlt = false;
boolean hasCtrl = false;
boolean hasShift = false;


void setup()
{
}


void keyPressed()
{
  if (key == CODED)
  {
    switch(keyCode)
    {
    case ALT:
      hasAlt = true;
      break;
    case CONTROL:
      hasCtrl = true;
      break;
    case SHIFT:
      hasShift = true;
      break;
    default:
      break;
    }
  } //
}


void keyReleased()
{
  if (key == CODED)
  {
    switch(keyCode)
    {
    case ALT:
      hasAlt = false;
      break;
    case CONTROL:
      hasCtrl = false;
      break;
    case SHIFT:
      hasShift = false;
      break;
    }
  }
}


void draw()
{
  if (keyPressed && key != 0)
  {
    if (hasAlt == true) print("<alt>");
    if (hasCtrl == true) print("<control>");
    if (hasShift == true) print("<shift>");

    if (key >= 'a' && key <= 'z')
    {
      println(key);
    } //
    else
    {
      //println("unknown key");
    }

    println(hex(key));
    
    // reset the key
    key = 0;
  }
}

The output in the console looks like below (comments added

<control>FFFF // 0xFFFF indicates coded key
<control>0001 // key for <ctrl>A
<control>0001 // key for <ctrl>A
<alt>FFFF // 0xFFFF indicates coded key
<alt>a // 'a'
0061 // hex value for 'a'

If you want to use the <ctrl> key, I suggest that you don’t use the complicated idea of my earlier suggestion but simply test the key for the values 0x0001 (<ctrl>A) to 0x001a (<ctrl>Z).

Thank you once again. I’ve tried the different variations below and none work. I don’t understand what I’m missing.

if(hasCtrl && hex(key) == hex(0061)){
     //draw text
      text("hasCtrl var true AND a in hex", 10, 10); 
    }
    
    if(hex(key) == hex(0x0001)){
     //draw text
      text("Ctrl+a in hex", 10, 10); 
    }

Try

if (key == 0x0001)

The hex() is only for printing.

And this is complicated; it does not compare what you think it compares :wink:

Thanks sterretje, that works but what was drawn was constantly flickering. By commenting out the key = 0; line of code, where you chose to reset the key, the flickering stopped. However, I still don’t understand why something like the following doesn’t work.

if(hasCtrl && key == 'a'){
     //draw text
      text("hasCtrl var true AND a key", 10, 10); 
    }
    
    if(hasCtrl && key == 0061){
     //hasCtrl var true AND 'a' key hex
     ellipse(90,90,20,20); //draw ellipse
    }

Also so far what works using the hex approach is just ctrl + something. Shift + something can be achieved by using just the uppercase key for the if’s conditional. I haven’t figured out a way to make alt + something work with their hex values because the produced hex values have letters sometimes and that generates an error. EDIT1: Figured it out, by placing “0x” before the hex code it works. EDIT2: cmd + something doesn’t output a unique hex value…?

2 Likes

:+1:

No idea; I’m not a Mac user.

Let’s first explain why I use the hasAlt, hasCtrl and hasShift flags. The variables key and keyCode reflect the last change. So if you press e.g. the alt key followed by the ‘a’ key (while keeping the alt key pressed), the system will only give you the ‘a’ key.

The below code displays the values for the keyCode and the key when a key (or key combination) is pressed. With this you can follow the sequence of what is happening.

boolean hasAlt = false;
boolean hasCtrl = false;
boolean hasShift = false;

void setup()
{
  // give the window the focus
  surface.setVisible(true);
}

void draw()
{
}

void keyPressed()
{
  println(millis() + "\t[keyPressed()]keyCode = " + hex(keyCode) + ", key = " + hex(key));
  //printKey("[keyPressed()]");
  if (key == CODED)
  {
    switch(keyCode)
    {
    case ALT:
      hasAlt = true;
      break;
    case CONTROL:
      hasCtrl = true;
      break;
    case SHIFT:
      hasShift = true;
      break;
    default:
      break;
    }
  }
}


void keyReleased()
{
  println(millis() + "\t[keyReleased()]keyCode = " + hex(keyCode) + ", key = " + hex(key));
  //printKey("[keyReleased()]");
  if (key == CODED)
  {
    switch(keyCode)
    {
    case ALT:
      hasAlt = false;
      break;
    case CONTROL:
      hasCtrl = false;
      break;
    case SHIFT:
      hasShift = false;
      break;
    }
  }
}

Some output examples. A key value of 0xFFFF indicates a coded key.
1a)

2752	[keyPressed()]keyCode = 0000005A, key = 007A    // 'z' key pressed
2851	[keyReleased()]keyCode = 0000005A, key = 007A   // 'z' key released

In this example you can see that the keyCode 0x5A (ascci code for capital ‘Z’) and the key is 0x7A (ascii code for lower case ‘z’).
1b)

5752	[keyPressed()]keyCode = 00000010, key = FFFF    // shift key pressed
5969	[keyPressed()]keyCode = 0000005A, key = 005A    // 'z' key pressed
6051	[keyReleased()]keyCode = 0000005A, key = 005A   // 'z' key released
6352	[keyReleased()]keyCode = 00000010, key = FFFF   // shift key released

The easy way to differentiate between capital ‘Z’ and lowercase ‘z’ is by testing the key directly

if(key == 'Z')  // 0x5A
{
}
if(key == 'z')  // 0x7A
{
}

The difficult way is to check the hasShift flag and the keyCode.

if(keyCode == 0x5a) // or if(keyCode == 'Z')
{
  // lowercase 'z'
  if(hasShift == false)
  {
  }
  // capital 'Z'
  else
  {
  }
}

2a)

4674	[keyPressed()]keyCode = 00000011, key = FFFF    // control key pressed
4941	[keyPressed()]keyCode = 00000041, key = 0001    // 'a' key pressed
5075	[keyReleased()]keyCode = 00000041, key = 0001   // 'a' key released
5241	[keyReleased()]keyCode = 00000011, key = FFFF   // control key released

You can see that ‘a’ was pressed based on the keyCode; 0x41 is the ascii code for capital ‘A’. Because of special meanings of a number of control keys (<ctrl>A to <ctrl>Z) the key is now 0x01.

The easy way to determine if <ctrl>A is pressed is to check the key

if(key == 0x01)  // ctrl-A
{
}

The difficult way is similar as the difficlt way above.

2b)

9139	[keyPressed()]keyCode = 00000012, key = FFFF    // alt key pressed
9424	[keyPressed()]keyCode = 00000041, key = 0061    // 'a' key pressed
9557	[keyReleased()]keyCode = 00000041, key = 0061   // 'a' key released
9922	[keyReleased()]keyCode = 00000012, key = FFFF   // alt key released

The only way to determine if the key is <alt>a is to use the hasAlt flag and either the keyCode or the key

if(hasAlt == true)
{
  if(key == 0x61) // lowercase 'a'
  {
  }
  if(key == 0x41) // capital 'A'
  {
  }
}

3)

2127	[keyPressed()]keyCode = 00000027, key = FFFF    // right arrow key pressed
2212	[keyReleased()]keyCode = 00000027, key = FFFF   // right arrow key released
9861	[keyPressed()]keyCode = 00000011, key = FFFF    // control key pressed
9977	[keyPressed()]keyCode = 00000027, key = FFFF    // right arrow key pressed
10027	[keyReleased()]keyCode = 00000027, key = FFFF   // right arrow key released
10227	[keyReleased()]keyCode = 00000011, key = FFFF   // control key released
13178	[keyPressed()]keyCode = 00000012, key = FFFF    // alt key pressed
13311	[keyPressed()]keyCode = 00000027, key = FFFF    // right arrow key pressed
13378	[keyReleased()]keyCode = 00000027, key = FFFF   // right arrow key released
13595	[keyReleased()]keyCode = 00000012, key = FFFF   // alt key released

0x27 is the keyCode for the right cursor key. In this example, all keys are coded keys (hence 0xFFFF) so you don’t have a means to differentiate between <right>, <ctrl><right> and <alt><right> on the key or the keyCode. You will have to use hasCtrl and hasAlt in combination with the keyCode.

Below the basics of how I set it up. There are three functions to handle the special keys and a function to handle the normal key. Those functions return a boolean that indicates if the function handled the key or not.

It only shows some examples of possible keys / key combinations. Replace the print statements in the handleXXX functions by whatever needs to be done.

boolean hasAlt = false;
boolean hasCtrl = false;
boolean hasShift = false;

void setup()
{
  // give the window the focus
  surface.setVisible(true);
}

void keyPressed()
{
  //println(millis() + "\t[keyPressed()]keyCode = " + hex(keyCode) + ", key = " + hex(key));
  if (key == CODED)
  {
    switch(keyCode)
    {
    case ALT:
      hasAlt = true;
      break;
    case CONTROL:
      hasCtrl = true;
      break;
    case SHIFT:
      hasShift = true;
      break;
    default:
      break;
    }
  }
}

void keyReleased()
{
  //println(millis() + "\t[keyReleased()]keyCode = " + hex(keyCode) + ", key = " + hex(key));
  if (key == CODED)
  {
    switch(keyCode)
    {
    case ALT:
      hasAlt = false;
      break;
    case CONTROL:
      hasCtrl = false;
      break;
    case SHIFT:
      hasShift = false;
      break;
    }
  }
}


void draw()
{
  boolean result = false;

  if (keyPressed)
  {
    result = handleAlt();
    if (result == false) result = handleCtrl();
    if (result == false) result = handleShift();
    if (result == false) result= handleNormal();

    // for debugging only
    if (result == false)
    {
      println("key was not handled");
    }
  }
}

/*
Handle alt key
 Returns:
 true if the key combination was handled, ekse false
 */
boolean handleAlt()
{
  if (hasAlt == false)
  {
    return false;
  }
  if (key == 'a')
  {
    println("<alt>a");
    //key = 0;
    return true;
  }
  if (key == 'A')
  {
    println("<alt>A");
    //key = 0;
    return true;
  }
  if (keyCode == RIGHT)
  {
    println("<alt><right>");
    //keyCode = 0;
    return true;
  }

  return false;
}

/*
Handle ctrl key
 Returns:
 true if the key combination was handled, ekse false
 */
boolean handleCtrl()
{
  if (hasCtrl == false)
  {
    return false;
  }
  /*
  // you can use this for <ctrl>A
  if (key == 0x01)
   {
   println("<ctrl>a (using key)");
   key = 0;
   return true;
   }
   */
  // or you can use this for <ctrl>A
  if (keyCode == 0x41)
  {
    println("<ctrl>a (using keyCode)");
    //keyCode = 0;
    return true;
  }
  if (keyCode == LEFT)
  {
    println("<ctrl><left>");
    //keyCode = 0;
    return true;
  }
  // there is no defined constant for the home key
  //  so the numeric value has to be used
  if (keyCode == 0x24)
  {
    println("<ctrl><home>");
    //keyCode = 0;
    return true;
  }

  return false;
}

/*
Handle shift key
 Returns:
 true if the key combination was handled, ekse false
 */
boolean handleShift()
{
  if (hasShift == false)
  {
    return false;
  }
  return false;
}

/*
Handle normal key
 Returns:
 true if the key was handled, ekse false
 */
boolean handleNormal()
{
  if (hasAlt == true || hasCtrl == true)
  {
    return false;
  }

  if (key != CODED)
  {
    println("normal key = " + hex(key));
    return true;
  }

  return false;
}
1 Like