root / Whitix / branches / scheduler / video / console.c

Revision 706, 14.4 kB (checked in by mwhitworth, 6 months ago)

Add CONSOLE_GET_POS ioctl.

Line 
1/*  This file is part of Whitix.
2 *
3 *  Whitix is free software; you can redistribute it and/or modify
4 *  it under the terms of the GNU General Public License as published by
5 *  the Free Software Foundation; either version 2 of the License, or
6 *  (at your option) any later version.
7 *
8 *  Whitix is distributed in the hope that it will be useful,
9 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 *  GNU General Public License for more details.
12 *
13 *  You should have received a copy of the GNU General Public License
14 *  along with Whitix; if not, write to the Free Software
15 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
16 *
17 */
18
19#include <console.h>
20#include <fs/devfs.h>
21#include <fs/vfs.h>
22#include <init.h>
23#include <error.h>
24#include <malloc.h>
25#include <module.h>
26#include <i386/ioports.h>
27#include <task.h>
28
29/* Device number */
30#define CONSOLE_MAJOR 1
31
32/* Read settings from a kernel command line soon */
33
34#define CONSOLE_WIDTH   80
35#define CONSOLE_BWIDTH  (CONSOLE_WIDTH*2)
36#define CONSOLE_HEIGHT  25
37#define CONSOLE_BHEIGHT (CONSOLE_HEIGHT*2)
38#define CONSOLE_SIZE    (CONSOLE_WIDTH * CONSOLE_HEIGHT *2)     /* Console size in bytes. */
39#define TAB_WIDTH       8
40#define CLEAR           ((0x07 << 8) | (' '))
41
42#define CONSOLE_BUF(i)  (scrBuf+(CONSOLE_SIZE*(i)))
43#define CONSOLE_IND(i,j) ((j)*CONSOLE_BWIDTH + (i))
44
45/* == GLOBALS == */
46static int curX,curY;
47static BYTE* screen=(BYTE*)0xB8000; /* VGA memory. */
48static BYTE* scrBuf;
49int currConsole=0;
50struct Console consoles[MAX_CONSOLES];
51Spinlock consoleLock;
52
53static void ConsoleNewLine();
54static void ConsoleUpdateCursorPos();
55static void ConsoleClearScreen();
56static void ConsoleWrite(char c,int index);
57static int ConsoleWriteString(int console,char* str,int length);
58
59int ChangeConsole(int index)
60{
61        if (consoles[index].flags & CONSOLE_GRAPHICS || (currConsole == index))
62                return 0;
63
64        SpinLockIrq(&consoleLock);
65
66        /* Multiple screens are set up during the boot sequence. */
67        if (!scrBuf || index < 0 || index >= MAX_CONSOLES)
68                goto error;
69
70        /* Copy current vga screen memory to scrBuf[currConsole] */
71        memcpy(CONSOLE_BUF(currConsole),screen,CONSOLE_SIZE);
72        consoles[currConsole].currX=curX;
73        consoles[currConsole].currY=curY;
74
75        currConsole=index;
76
77        /* And copy scrBuf[currConsole] into VGA screen memory */
78        memcpy(screen,CONSOLE_BUF(currConsole), CONSOLE_SIZE);
79        curX=consoles[currConsole].currX;
80        curY=consoles[currConsole].currY;
81
82        ConsoleUpdateCursorPos();
83
84error:
85        SpinUnlockIrq(&consoleLock);
86        return 0;
87}
88
89SYMBOL_EXPORT(ChangeConsole);
90
91static void ConsoleScrollScreen(int index)
92{
93        int i;
94        DWORD flags;
95
96        if (consoles[index].flags & CONSOLE_GRAPHICS)
97                return ;
98
99        IrqSaveFlags(flags);
100 
101        if (currConsole == index)
102        {
103                for (i=0; i < CONSOLE_HEIGHT-1 ; i++)
104                        memcpy ( &screen [ CONSOLE_IND(0,i) ] , &screen [ CONSOLE_IND(0,i+1) ] , CONSOLE_BWIDTH);
105               
106                /* Clear the last line of the console. */
107                memsetw( & screen[ CONSOLE_IND(0,i) ] , CONSOLE_WIDTH, CLEAR);
108        }else{
109                for (i=0; i<CONSOLE_HEIGHT-1; i++)
110                        memcpy ( & CONSOLE_BUF(index) [ CONSOLE_IND(0,i) ] ,
111                                & CONSOLE_BUF(index) [ CONSOLE_IND(0,i) ], CONSOLE_BWIDTH);
112                       
113                /* Clear last line */
114                memsetw( & CONSOLE_BUF(index)[ CONSOLE_IND(0,i) ] , CONSOLE_WIDTH, CLEAR);
115        }
116
117        IrqRestoreFlags(flags);
118}
119
120void putc(int c)
121{
122        ConsoleWrite(c,currConsole);
123}
124
125unsigned char colorTable[] = {0, 4, 2, 6, 1, 5, 3, 7};
126
127int ConsoleWriteString(int console, char* str, int length)
128{
129        struct Console* currCons=&consoles[console];
130        DWORD flags;
131        int oldLength=length;
132
133        if (currCons->flags & CONSOLE_GRAPHICS)
134                return 0;
135
136        IrqSaveFlags(flags);
137
138        while (length--)
139        {
140                if (*str == 0x1B)
141                {
142                        currCons->inCtrl=1;
143                        goto end;
144                }
145
146                if (currCons->inCtrl)
147                {
148                        if (*str == '[')
149                        {
150                                currCons->ctrlParams=1;
151                                ZeroMemory(currCons->params, sizeof(unsigned int)*16);
152                                currCons->paramIndex=0;
153                                goto end;
154                        }
155                       
156                        if (currCons->ctrlParams)
157                        {
158                                if (*str == ';')
159                                {
160                                        ++currCons->paramIndex;
161                                        goto end;
162                                }else if (*str < '0' || *str > '9') /* Command */
163                                {
164                                        currCons->cmdLetter=1;
165                                        currCons->ctrlParams=0;
166                                }else{
167                                        currCons->params[currCons->paramIndex]*=10;
168                                        currCons->params[currCons->paramIndex]+=(*str)-'0';
169                                        goto end;
170                                }
171                        }
172
173                        /* VT100 control characters are handled here. */
174                        if (currCons->cmdLetter)
175                        {
176                                switch (*str)
177                                {
178                                        /* 'J'. Clear the screen. */
179                                        case 'J':
180                                                ConsoleClearScreen(console);
181                                                break;
182
183                                        /* 'H'. Move the cursor to the specified position */
184                                        case 'H':
185
186                                                /* Bound the given parameters. */
187                                                if (currCons->params[0] >= CONSOLE_WIDTH)
188                                                        currCons->params[0]=CONSOLE_WIDTH;
189
190                                                if (currCons->params[1] >= CONSOLE_HEIGHT)
191                                                        currCons->params[1]=CONSOLE_HEIGHT;
192
193                                                /* Update the cursor position. */
194                                                if (currConsole == console)
195                                                {
196                                                        curX=currCons->params[0];
197                                                        curY=currCons->params[1];
198                                                        ConsoleUpdateCursorPos();
199                                                }else{
200                                                        currCons->currX=currCons->params[0];
201                                                        currCons->currY=currCons->params[1];
202                                                }
203
204                                                break;
205
206                                        /* 'K'. Erase characters from console. */
207                                        case 'K':
208                                        {
209                                                switch (currCons->params[0])
210                                                {
211
212                                                /* From current cursor position to end of line */
213                                                case 0:
214                                                        if (currConsole == console)
215                                                                memsetw((WORD*)(screen+(curY*(CONSOLE_WIDTH*2))+(curX*2)),CONSOLE_WIDTH-curX,(currCons->currColor << 8) | ' ');
216                                                        else
217                                                                memsetw((WORD*)(scrBuf+(CONSOLE_WIDTH*CONSOLE_HEIGHT*2*console)+(currCons->currY*(CONSOLE_WIDTH*2))+currCons->currX),CONSOLE_WIDTH-currCons->currX,(currCons->currColor << 8) | ' ');
218                                                        break;
219
220                                                /* From start of line to current cursor position */
221                                                case 1:
222                                                        if (currConsole == console)
223                                                                memsetw((WORD*)(screen+(curY*(CONSOLE_WIDTH*2))),curX,(currCons->currColor << 8) | ' ');
224                                                        else
225                                                                memsetw((WORD*)(scrBuf+(CONSOLE_WIDTH*CONSOLE_HEIGHT*2*console)+(currCons->currY*(CONSOLE_WIDTH*2))),curX,(currCons->currColor << 8) | ' ');
226                                                        break;
227
228                                                case 2:
229                                                        /* Erase whole of current line */
230                                                        if (currConsole == console)
231                                                                memsetw((WORD*)(screen+(curY*(CONSOLE_WIDTH*2))),CONSOLE_WIDTH,(currCons->currColor << 8) | ' ');
232                                                        else
233                                                                memsetw((WORD*)(scrBuf+(CONSOLE_WIDTH*CONSOLE_HEIGHT*2*console)+(currCons->currY*(CONSOLE_WIDTH*2))),CONSOLE_WIDTH,(currCons->currColor << 8) | ' ');
234                                                        break;
235                                                }
236                                                break;
237                                        }
238
239                                        /* 'm'. Set graphic rendition. */
240                                        case 'm':
241                                                switch (currCons->params[0])
242                                                {
243                                                        /* Reset the color */
244                                                        case 0:
245                                                                currCons->currColor=7;
246                                                                break;
247
248                                                        /* Invert the color */
249                                                        case 7:
250                                                                currCons->currColor=(currCons->currColor & 0x88) | (((currCons->currColor >> 4) | (currCons->currColor << 4)) & 0x77);                                                         
251                                                                break;
252
253                                                        case 30 ... 37:
254                                                                currCons->currColor=colorTable[currCons->params[0] % 10];
255                                                                break;
256                                                };
257
258                                                break;
259                                }
260
261                                currCons->inCtrl=currCons->cmdLetter=0;
262                                goto end;
263                        }
264                }
265
266                ConsoleWrite(*str,console);
267end:
268                str++;
269        }
270
271        IrqRestoreFlags(flags);
272
273        if (console == currConsole)
274                ConsoleUpdateCursorPos();
275
276        return oldLength;
277}
278
279SYMBOL_EXPORT(ConsoleWriteString);
280
281extern void KeyboardDoAlarm();
282
283/* Writes an ASCII character to screen */
284
285static void ConsoleWrite(char c,int index)
286{
287        struct Console* cons=&consoles[index];
288
289        if (cons->flags & CONSOLE_GRAPHICS)
290                return ;
291
292        switch (c)
293        {
294        case '\r':
295                break;
296
297        case '\n':
298                ConsoleNewLine(index);
299                break;
300       
301        case '\b':
302                if (currConsole == index && curX > 0)
303                {
304                        curX--;
305                        screen[(curY*(CONSOLE_WIDTH*2))+(curX*2)]=' ';
306                        screen[(curY*(CONSOLE_WIDTH*2))+(curX*2)+1]=cons->currColor;
307                }else if (cons->currX > 0)
308                {
309                        cons->currX--;
310                        scrBuf[(CONSOLE_WIDTH*CONSOLE_HEIGHT*2*index)+(cons->currY*(CONSOLE_WIDTH*2))+(cons->currX*2)]=' ';
311                        scrBuf[(CONSOLE_WIDTH*CONSOLE_HEIGHT*2*index)+(cons->currY*(CONSOLE_WIDTH*2))+(cons->currY*2)+1]=cons->currColor;
312                }
313                break;
314
315        case '\t':
316        {
317                if (currConsole == index)
318                {
319                        int i;
320                        for (i=0; i<TAB_WIDTH*2; i+=2)
321                        {
322                                screen[(curY*(CONSOLE_WIDTH*2))+(curX*2)+i]=' ';
323                                screen[(curY*(CONSOLE_WIDTH*2))+(curX*2)+1+i]=cons->currColor;
324                                ++curX;
325                                if (curX >= CONSOLE_WIDTH)
326                                        ConsoleNewLine(currConsole);
327                        }
328                }else{
329                        int i;
330                        for (i=0; i<TAB_WIDTH*2; i+=2)
331                        {
332                                scrBuf[(CONSOLE_WIDTH*CONSOLE_HEIGHT*2*index)+(cons->currY*(CONSOLE_WIDTH*2))+(cons->currX*2)+i]=' ';
333                                scrBuf[(CONSOLE_WIDTH*CONSOLE_HEIGHT*2*index)+(cons->currY*(CONSOLE_WIDTH*2))+(cons->currX*2)+i+1]=cons->currColor;
334                                ++cons->currX;
335                                if (cons->currX >= CONSOLE_WIDTH)
336                                        ConsoleNewLine(index);
337                        }
338                }
339                break;
340        }
341
342        default:
343                if (currConsole == index)
344                {
345                        screen[(curY*(CONSOLE_WIDTH*2))+(curX*2)]=c;
346                        screen[(curY*(CONSOLE_WIDTH*2))+(curX*2)+1]=cons->currColor;
347                        curX++;
348                        if (curX >= CONSOLE_WIDTH)
349                                ConsoleNewLine(currConsole);
350                }else{
351                        CONSOLE_BUF(index)[CONSOLE_IND(cons->currX*2, cons->currY)]=c;
352                        CONSOLE_BUF(index)[CONSOLE_IND(cons->currX*2, cons->currY)+1]=cons->currColor;
353                        ++cons->currX;
354                        if (cons->currX >= CONSOLE_WIDTH)
355                                ConsoleNewLine(index);
356                }
357        }
358}
359
360void ConsoleWriteOutput(char* message, int length)
361{
362        ConsoleWriteString(currConsole, message, length);
363}
364
365static void ConsoleClearScreen(int index)
366{
367        if (consoles[index].flags & CONSOLE_GRAPHICS)
368                return;
369
370        int i;
371        if (currConsole == index)
372        {
373                for (i=0; i<(CONSOLE_WIDTH*CONSOLE_HEIGHT*2); i+=2)
374                {
375                        screen[i]=' ';
376                        screen[i+1]=7;
377                }
378
379                curX=0;
380                curY=0;
381                ConsoleUpdateCursorPos();
382        }else{
383                for (i=0; i<(CONSOLE_WIDTH*CONSOLE_HEIGHT*2); i+=2)
384                {
385                        scrBuf[(CONSOLE_WIDTH*CONSOLE_HEIGHT*2*index)+i]=' ';
386                        scrBuf[(CONSOLE_WIDTH*CONSOLE_HEIGHT*2*index)+i+1]=7;
387                }
388
389                consoles[index].currX=0;
390                consoles[index].currY=0;
391        }
392}
393
394void ConsoleClearCurrent()
395{
396        ConsoleClearScreen(currConsole);
397}
398
399static void ConsoleUpdateCursorPos()
400{
401        DWORD flags;
402        WORD position;
403        IrqSaveFlags(flags);
404
405        position=(curY*(CONSOLE_WIDTH))+curX;
406
407        outb(0x3D4,14);
408        outb(0x3D5,(BYTE)(position >> 8));
409        outb(0x3D4,15);
410        outb(0x3D5,(BYTE)(position & 0xFF));
411
412        IrqRestoreFlags(flags);
413}
414
415static void ConsoleGetCursorPos()
416{
417        DWORD flags;
418        WORD position;
419
420        IrqSaveFlags(flags);
421
422        outb(0x3D4, 14);
423        position=inb(0x3D5) << 8;
424        outb(0x3D4, 15);
425        position |= inb(0x3D5);
426
427        IrqRestoreFlags(flags);
428
429        curX=position % 80;
430        curY=position/80;
431}
432
433static void ConsoleNewLine(int index)
434{
435        if (currConsole == index)
436        {
437                curX=0;
438                ++curY;
439                if (curY == CONSOLE_HEIGHT)
440                {
441                        curY=CONSOLE_HEIGHT-1;
442                        ConsoleScrollScreen(index);
443                }
444        }else{
445                consoles[index].currX=0;
446                ++consoles[index].currY;
447                if (consoles[index].currY == CONSOLE_HEIGHT)
448                {
449                        consoles[index].currY=CONSOLE_HEIGHT-1;
450                        ConsoleScrollScreen(index);
451                }
452        }
453}
454
455void ConsoleAddKey(BYTE n)
456{
457        BYTE head;
458        DWORD flags;
459        struct Console* currCons;
460
461        /* May not press keys before console array has been set up */
462        if (!scrBuf)
463                return;
464
465        currCons=&consoles[currConsole];
466
467        if (!currCons)
468                return;
469
470        if (!n)
471                return;
472
473        IrqSaveFlags(flags);
474
475        head=(currCons->head+1) % 25;
476        if (head != currCons->tail)
477        {
478                currCons->keyboardBuf[currCons->head]=n;
479                currCons->head=head;
480                WakeUp(&currCons->waitQueue);
481        }
482
483        IrqRestoreFlags(flags);
484}
485
486SYMBOL_EXPORT(ConsoleAddKey);
487
488void ConsoleAddKeyCode(BYTE key)
489{
490        if (consoles[currConsole].flags & CONSOLE_SENDKEYC)
491                ConsoleAddKey(key);
492}
493
494SYMBOL_EXPORT(ConsoleAddKeyCode);
495
496int ConsoleEarlyInit()
497{
498        ConsoleClearScreen(0);
499        consoles[0].currColor=7;
500        return 0;
501}
502
503BYTE ConsoleGetCharacter()
504{
505        BYTE retVal=0;
506        struct Console* currCons=&consoles[currConsole];
507
508        if (currCons->head != currCons->tail)
509        {
510                retVal=currCons->keyboardBuf[currCons->tail] & 0xFF;
511                currCons->tail=(currCons->tail+1) % 25; /* Wraps around */
512                if (!(currCons->flags & CONSOLE_NO_ECHO) && retVal < 0x80 && current && retVal != '\b') DoWriteFile(&current->files[0],&retVal,1);
513        }
514
515        return retVal;
516}
517
518int ConsoleDevRead(struct File* file,BYTE* data,DWORD size)
519{
520        int index=file->vNode->devId.minor;
521
522        struct Console* currCons=&consoles[index];
523        int i=0;
524        BYTE keyCode=0;
525        DWORD flags;
526
527        IrqSaveFlags(flags);
528
529        do
530        {
531                WAIT_ON(consoles[index].waitQueue, currCons->head != currCons->tail);
532                data[i++]=ConsoleGetCharacter();
533        }while (i < size);
534
535        IrqRestoreFlags(flags);
536
537        return size;
538}
539
540int ConsoleDevWrite(struct File* file,BYTE* data,DWORD size)
541{
542        return ConsoleWriteString(file->vNode->devId.minor,(char*)data,size);
543}
544
545int ConsoleDevSeek(struct File* file,int position,int whence)
546{
547        return -EINVAL;
548}
549
550int ConsoleIoCtl(struct File* file,unsigned long code,char* data)
551{
552        int index=file->vNode->devId.minor;
553
554        switch (code)
555        {
556        case CONSOLE_SET_OPTIONS:
557                consoles[index].flags=(int)data;
558                break;
559
560        case CONSOLE_GET_OPTIONS:
561                *(int*)data=consoles[index].flags;
562                break;
563
564        case CONSOLE_GET_INFO:
565        {
566                struct ConsoleInfo* info=(struct ConsoleInfo*)data; /* TODO: Check this is ok to access. */
567                info->rows=CONSOLE_HEIGHT;
568                info->cols=CONSOLE_WIDTH;
569                break;
570        }
571
572        case CONSOLE_GET_POS:
573        {
574                struct ConsoleInfo* info=(struct ConsoleInfo*)data;
575                if (index == currConsole)
576                {
577                        info->rows=curY;
578                        info->cols=curX;
579                }else{
580                        info->rows=consoles[index].currY;
581                        info->cols=consoles[index].currX;
582                }
583
584                break;
585        }
586
587        default:
588                return -EINVAL;
589        }
590
591        return 0;
592}
593
594int ConsolePoll(struct File* file, struct PollItem* item, struct PollQueue* pollQueue)
595{
596        struct Console* cons=&consoles[file->vNode->devId.minor];
597
598        if (item->events & POLL_IN && cons->head != cons->tail)
599                item->revents |= POLL_IN;
600
601        if (item->events & POLL_OUT)
602                item->revents |= POLL_OUT;
603
604        PollAddWait(pollQueue, &cons->waitQueue);
605
606        return 0;
607}
608
609struct FileOps consoleOps={
610        .read = ConsoleDevRead, /* Read the keyboard buffer */
611        .write = ConsoleDevWrite,
612        .seek = ConsoleDevSeek,
613        .ioctl = ConsoleIoCtl,
614        .poll = ConsolePoll,
615};
616
617int ConsoleInit()
618{
619        int i;
620        char buf[32];
621
622        /* Now allocate all the other consoles, since dynamic memory allocation can be performed. */
623        scrBuf=(BYTE*)malloc(MAX_CONSOLES*(CONSOLE_WIDTH*CONSOLE_HEIGHT*2));
624        if (!scrBuf)
625        {
626                KernelPanic("Failed to allocate memory for multiple consoles");
627                return -ENOMEM; /* Never reached. */
628        }
629
630        ZeroMemory(scrBuf,(MAX_CONSOLES*(CONSOLE_WIDTH*CONSOLE_HEIGHT*2)));
631
632        /* Register all the consoles */
633        for (i=0; i<MAX_CONSOLES; i++)
634        {
635                sprintf(buf,"Consoles/Console%d",i);
636                DevAddDevice(buf,CONSOLE_MAJOR,i,DEVICE_CHAR,&consoleOps);
637                INIT_LIST_HEAD(&consoles[i].waitQueue.list);
638                consoles[i].currColor=7;
639                consoles[i].head=consoles[i].tail=0;
640        }
641
642        /* Get the current cursor position, and take over from the early console driver. */
643        ConsoleGetCursorPos();
644
645        KeSetOutput(ConsoleWriteOutput);
646
647        return 0;
648}
649
650DeviceInit(ConsoleInit);
Note: See TracBrowser for help on using the browser.