├── .gitattributes ├── source ├── options.txt ├── uniCorpus.txt ├── main.h ├── PosChart.h ├── ValidChar.h ├── openGL.h ├── enums.h ├── Button.h ├── main.cpp ├── ButtonSet.h ├── MouseStatus.h ├── Keyboard.h ├── Predictor.h ├── DiskPart.h ├── Trie.h ├── KeyMain.h └── Drawer.h ├── SliceType.zip └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.zip binary 4 | -------------------------------------------------------------------------------- /source/options.txt: -------------------------------------------------------------------------------- 1 | Select Time (ms): 2 | 1000 3 | Animation Time (ms): 4 | 250 -------------------------------------------------------------------------------- /SliceType.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbenligiray/slicetype/HEAD/SliceType.zip -------------------------------------------------------------------------------- /source/uniCorpus.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbenligiray/slicetype/HEAD/source/uniCorpus.txt -------------------------------------------------------------------------------- /source/main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_H 2 | #define MAIN_H 3 | 4 | #include "openGL.h" 5 | #include "KeyMain.h" 6 | 7 | void runMainLoop(int val); 8 | void update(); 9 | void render(); 10 | 11 | void handleMousePos(int x, int y); 12 | void handleMouseClick(int button, int state, int x, int y); 13 | void handleKeys(unsigned char key, int x, int y); 14 | 15 | KeyMain kbMain; 16 | 17 | using std::string; 18 | using std::cout; 19 | using std::endl; 20 | 21 | 22 | #endif -------------------------------------------------------------------------------- /source/PosChart.h: -------------------------------------------------------------------------------- 1 | #ifndef POSCHART_H 2 | #define POSCHART_H 3 | 4 | #include 5 | #include 6 | 7 | using std::fill_n; 8 | using std::vector; 9 | 10 | class PosChart 11 | { 12 | vector loop0; 13 | vector loop1; 14 | vector loop2; 15 | public: 16 | PosChart(); 17 | void fillPos(int loop, int row); 18 | const vector& PosChart::getLoop(int loop) const; 19 | }; 20 | 21 | 22 | 23 | PosChart::PosChart() 24 | { 25 | //initialize all elements with 0 26 | loop0 = vector(2, 0); 27 | loop1 = vector(12, 0); 28 | loop2 = vector(12, 0); 29 | } 30 | 31 | void PosChart::fillPos(int loop, int row) 32 | { 33 | switch (loop) 34 | { 35 | case 0: 36 | loop0[row] = 1; 37 | break; 38 | case 1: 39 | loop1[row] = 1; 40 | break; 41 | case 2: 42 | loop2[row] = 1; 43 | break; 44 | } 45 | } 46 | 47 | const vector& PosChart::getLoop(int loop) const 48 | { 49 | switch (loop) 50 | { 51 | case 0: 52 | return loop0; 53 | case 1: 54 | return loop1; 55 | case 2: 56 | return loop2; 57 | } 58 | } 59 | 60 | #endif -------------------------------------------------------------------------------- /source/ValidChar.h: -------------------------------------------------------------------------------- 1 | #ifndef VALIDCHAR_H 2 | #define VALIDCHAR_H 3 | 4 | #include 5 | 6 | using std::string; 7 | 8 | //holds a char and its matching prediction 9 | class ValidChar 10 | { 11 | char character; 12 | string prediction; 13 | unsigned int freq; 14 | public: 15 | ValidChar(); 16 | ValidChar(char ch, string str, unsigned int fq); 17 | const char ValidChar::getChar() const; 18 | const string& getString() const; 19 | const unsigned int getFreq() const; 20 | void updatePred(string newPred, unsigned int newFreq); 21 | string getPrediction(); 22 | }; 23 | 24 | ValidChar::ValidChar() 25 | { 26 | character = ' '; 27 | prediction = ""; 28 | freq = 0; 29 | } 30 | 31 | ValidChar::ValidChar(char ch, string str, unsigned int fq) 32 | { 33 | character = ch; 34 | prediction = str; 35 | freq = fq; 36 | } 37 | 38 | const char ValidChar::getChar() const 39 | { 40 | return character; 41 | } 42 | const string& ValidChar::getString() const 43 | { 44 | return prediction; 45 | } 46 | const unsigned int ValidChar::getFreq() const 47 | { 48 | return freq; 49 | } 50 | void ValidChar::updatePred(string newPred, unsigned int newFreq) 51 | { 52 | prediction = newPred; 53 | freq = newFreq; 54 | } 55 | 56 | string ValidChar::getPrediction() 57 | { 58 | return prediction; 59 | } 60 | 61 | #endif -------------------------------------------------------------------------------- /source/openGL.h: -------------------------------------------------------------------------------- 1 | #ifndef OPENGL_H 2 | #define OPENGL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "enums.h" 11 | 12 | void initGL() 13 | { 14 | //Create OpenGL 2.1 context 15 | glutInitContextVersion(2, 1); 16 | 17 | //Create Double Buffered Window with multisampled buffer 18 | glutInitDisplayMode(GLUT_DOUBLE | GLUT_MULTISAMPLE); 19 | glutInitWindowSize(SCREEN_SIZE + 16, SCREEN_SIZE + 16); 20 | glutCreateWindow("SliceType"); 21 | 22 | //Initialize GLEW 23 | glewInit(); 24 | 25 | //Set the viewport 26 | glViewport(0.f, 0.f, SCREEN_SIZE, SCREEN_SIZE); 27 | 28 | //Initialize Projection Matrix 29 | glMatrixMode(GL_PROJECTION); 30 | glLoadIdentity(); 31 | glOrtho(0.0, SCREEN_SIZE + 8, SCREEN_SIZE, -8, 1.0, -1.0); 32 | 33 | //Initialize Modelview Matrix 34 | glMatrixMode(GL_MODELVIEW); 35 | glLoadIdentity(); 36 | 37 | //Initialize clear color 38 | glClearColor(0.f, 0.f, 0.f, 1.f); 39 | 40 | //Set blending 41 | glEnable(GL_BLEND); 42 | glDisable(GL_DEPTH_TEST); 43 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 44 | 45 | //Set antialiasing/multisampling 46 | glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); 47 | glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); 48 | glEnable(GL_LINE_SMOOTH); 49 | glEnable(GL_POLYGON_SMOOTH); 50 | glEnable(GL_MULTISAMPLE); 51 | glDisable(GL_TEXTURE_2D); 52 | } 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /source/enums.h: -------------------------------------------------------------------------------- 1 | #ifndef ENUMS_H 2 | #define ENUMS_H 3 | 4 | 5 | int SCREEN_SIZE = 920; 6 | int SCREEN_SIZE_X = SCREEN_SIZE; 7 | int SCREEN_SIZE_Y = SCREEN_SIZE; 8 | const int SCREEN_FPS = 60; 9 | const float radi0 = SCREEN_SIZE / 7.f; 10 | const float radi1 = SCREEN_SIZE / 3.f; 11 | const float radi2 = SCREEN_SIZE / 2.f; 12 | 13 | const double circleBoundary = 1.f; 14 | const double lineWidth = 2.f; 15 | const int slices = 40; 16 | const int loops = 10; 17 | 18 | const float charScale = (0.225 / 640) * SCREEN_SIZE; 19 | const float predScale = (0.15 / 640) * SCREEN_SIZE; 20 | const float textWidth = 3.f; 21 | const float textSmallWidth = 2.f; 22 | float selectTime; 23 | float morphTime; 24 | 25 | int selectCount; 26 | int morphCount; 27 | 28 | const float textSpacing = 2.f; 29 | 30 | enum colorTypes { cornflowerBlue, paleOrange, orange, black, green, purple, coral, warmgrey, olivedrab, goldenrod, turquoise }; 31 | enum lateralMergePhases { firstPhase, secondPhase, thirdPhase }; 32 | enum kbState { noSelect, select1, select2, selectOuter, selectOtherKey, selectingOtherKey, secondClear }; 33 | 34 | colorTypes colorFront = turquoise; 35 | colorTypes colorBack = paleOrange; 36 | colorTypes colorS1 = orange; 37 | colorTypes colorS2 = green; 38 | 39 | 40 | 41 | //the first two goes in the middle ring, a total of 26 chars for each screen 42 | static const char firstScreenChars[] = { 'e', 't', 43 | 'a', 'o', 'i', 'n', 's', 'r', 'h', 'l', 'd', 'c', 'u', 'm', 44 | 'f', 'p', 'g', 'w', 'y', 'b', 'v', 'k', 'x', 'j', 'z', 'q' }; 45 | 46 | static const char secondScreenChars[] = { '.', ',', 47 | '\'', '?', '!', ':', ';', '"', '$', '&', '/', '-', '%', '#', 48 | '(', ')', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; 49 | 50 | 51 | 52 | #endif -------------------------------------------------------------------------------- /source/Button.h: -------------------------------------------------------------------------------- 1 | #ifndef BUTTON_H 2 | #define BUTTON_H 3 | 4 | #include "DiskPart.h" 5 | #include "ValidChar.h" 6 | #include "PosChart.h" 7 | #include "enums.h" 8 | 9 | int getPosOfChar(const char *array, char c); 10 | void getLocOfChar(char c, int &loop, int &row); 11 | 12 | //Holds every aspect of a button 13 | class Button 14 | { 15 | DiskPart buttonShape; 16 | ValidChar writing; 17 | 18 | public: 19 | Button(); 20 | Button(ValidChar inpWriting); 21 | DiskPart& getButtonShape(); 22 | ValidChar& getWriting(); 23 | }; 24 | 25 | Button::Button() 26 | { 27 | } 28 | 29 | Button::Button(ValidChar inpWriting) 30 | { 31 | writing = inpWriting; 32 | 33 | int loop, row; 34 | getLocOfChar(writing.getChar(), loop, row); 35 | 36 | buttonShape = DiskPart(loop, row); 37 | } 38 | 39 | DiskPart& Button::getButtonShape() 40 | { 41 | return buttonShape; 42 | } 43 | 44 | ValidChar& Button::getWriting() 45 | { 46 | return writing; 47 | } 48 | 49 | int getPosOfChar(const char *array, char c) 50 | { 51 | size_t size = 26; //not cool 52 | const char* end = array + size; 53 | const char* match = std::find(array, end, c); 54 | return (end == match) ? -1 : (match - array); 55 | } 56 | 57 | void getLocOfChar(char c, int &loop, int &row) 58 | { 59 | int noOfChar = getPosOfChar(firstScreenChars, c); 60 | 61 | if (noOfChar == -1) 62 | noOfChar = getPosOfChar(secondScreenChars, c); 63 | 64 | //middle loop 65 | if (noOfChar < 2) 66 | { 67 | loop = 0; 68 | //first half 69 | if (noOfChar == 0) 70 | row = 0; 71 | //second half 72 | else 73 | row = 1; 74 | } 75 | //second ring 76 | else if (noOfChar < 14) 77 | { 78 | loop = 1; 79 | row = (noOfChar - 2) % 12; 80 | } 81 | //third ring 82 | else 83 | { 84 | loop = 2; 85 | row = (noOfChar - 2) % 12; 86 | } 87 | } 88 | 89 | 90 | #endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## SliceType: Fast Gaze Typing with a Merging Keyboard 2 | 3 | The soft keyboard designed for gaze typing proposed in the following paper: 4 | 5 | [Benligiray, B.; Topal, C.; Akinlar, C., "SliceType: Fast gaze typing with a merging keyboard," Journal on Multimodal Interfaces, 2018.](https://arxiv.org/abs/1706.02499) 6 | 7 | The .zip file contains the soft keyboard built for Windows. 8 | I also uploaded the source code to refer to it myself. 9 | I don't provide a makefile at the moment, but it only needs freeglut to build. 10 | It uses Win32 API, so it will take some work to port. 11 | 12 | #### What is this? 13 | 14 | * SliceType is a soft keyboard designed for fast and comfortable gaze typing. 15 | * Our main objective in designing SliceType was to use the screen are efficiently. We achieved this by determining the keys that will not be used for the next input, and allocating their space to adjacent keys with a merging animation. 16 | * As a result of the key merging functionality, the target keys are more likely to be larger. Large keys are fast to navigate towards, and easy to dwell on even, when there is eye tracking jitter. 17 | * In addition, Slicetype uses a word completion proposal and selection scheme that is ideal for gaze typing by dwelling. 18 | * The advantages of the features above are quantified using the Fitts' law. 19 | * SliceType is compared with two other gaze typing keyboards, Dasher and GazeTalk. The user experiment done with 37 novice users showed that, SliceType provided the highest text entry rate, and is preferred over the other keyboards. 20 | 21 |

22 | 23 |

24 | 25 | #### Videos 26 | 27 | [SliceType: Fast Gaze Typing with a Merging Keyboard](https://www.youtube.com/watch?v=3IM0-utYrDg) 28 | 29 | [SliceType - Advanced User Performance](https://www.youtube.com/watch?v=um75h3hHcwo) 30 | 31 | [Slicetype - Gaze Typing Example (Turkish)](https://www.youtube.com/watch?v=WlAcx398p-A) 32 | 33 | -------------------------------------------------------------------------------- /source/main.cpp: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | //text addition 4 | #include 5 | #include 6 | //text addition 7 | 8 | int main(int argc, char* args[]) 9 | { 10 | 11 | string line; 12 | ifstream myfile("options.txt"); 13 | std::istringstream convert; 14 | int number = 1000; 15 | if (myfile.is_open()) 16 | { 17 | getline(myfile, line); 18 | getline(myfile, line); 19 | convert = std::istringstream(line); 20 | convert >> number; 21 | selectTime = (float)number / 1000; 22 | 23 | getline(myfile, line); 24 | getline(myfile, line); 25 | convert = std::istringstream(line); 26 | convert >> number; 27 | morphTime = (float)number / 1000; 28 | 29 | myfile.close(); 30 | 31 | selectCount = (int)(selectTime * SCREEN_FPS); 32 | morphCount = (int)(morphTime * SCREEN_FPS); 33 | } 34 | 35 | else 36 | return 1; 37 | 38 | glutInit(&argc, args); 39 | initGL(); 40 | 41 | //Set functions 42 | glutDisplayFunc(render); 43 | glutPassiveMotionFunc(handleMousePos); 44 | glutMouseFunc(handleMouseClick); 45 | glutKeyboardFunc(handleKeys); 46 | 47 | //Set loop 48 | glutTimerFunc(1000 / SCREEN_FPS, runMainLoop, 0); 49 | glutMainLoop(); 50 | 51 | 52 | 53 | return 0; 54 | } 55 | 56 | void runMainLoop(int val) 57 | { 58 | render(); 59 | 60 | glutTimerFunc(1000 / SCREEN_FPS, runMainLoop, val); 61 | } 62 | 63 | 64 | 65 | void render() 66 | { 67 | 68 | kbMain.tickCounter(); 69 | kbMain.TEMPDRAW(); 70 | glutSwapBuffers(); 71 | } 72 | 73 | void handleMousePos(int x, int y) 74 | { 75 | kbMain.updateMouseCell(x, y); 76 | } 77 | 78 | void handleMouseClick(int button, int state, int x, int y) 79 | { 80 | switch (button) 81 | { 82 | case 0: 83 | cout << "left button "; 84 | break; 85 | case 2: 86 | cout << "right button "; 87 | break; 88 | default: 89 | return; 90 | } 91 | switch (state) 92 | { 93 | case 0: 94 | cout << "down" << endl; 95 | break; 96 | case 1: 97 | cout << "up" << endl; 98 | break; 99 | } 100 | } 101 | 102 | void handleKeys(unsigned char key, int x, int y) 103 | { 104 | cout << key << endl; 105 | } 106 | 107 | -------------------------------------------------------------------------------- /source/ButtonSet.h: -------------------------------------------------------------------------------- 1 | #ifndef BUTTONSET_H 2 | #define BUTTONSET_H 3 | 4 | #include 5 | #include "Button.h" 6 | #include "PosChart.h" 7 | #include "ValidChar.h" 8 | #include "enums.h" 9 | 10 | 11 | using std::vector; 12 | 13 | //set of active buttons 14 | class ButtonSet 15 | { 16 | vector