pyOS-kernel 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,319 @@
1
+ """
2
+ pyOS Keyboard Driver - PS/2 Keyboard Support
3
+ """
4
+
5
+ from typing import Optional, Callable, Dict
6
+ from enum import Enum
7
+ from dataclasses import dataclass
8
+
9
+
10
+ class KeyCode(Enum):
11
+ """Keyboard scan codes"""
12
+ ESC = 0x01
13
+ KEY_1 = 0x02
14
+ KEY_2 = 0x03
15
+ KEY_3 = 0x04
16
+ KEY_4 = 0x05
17
+ KEY_5 = 0x06
18
+ KEY_6 = 0x07
19
+ KEY_7 = 0x08
20
+ KEY_8 = 0x09
21
+ KEY_9 = 0x0A
22
+ KEY_0 = 0x0B
23
+ MINUS = 0x0C
24
+ EQUALS = 0x0D
25
+ BACKSPACE = 0x0E
26
+ TAB = 0x0F
27
+ KEY_Q = 0x10
28
+ KEY_W = 0x11
29
+ KEY_E = 0x12
30
+ KEY_R = 0x13
31
+ KEY_T = 0x14
32
+ KEY_Y = 0x15
33
+ KEY_U = 0x16
34
+ KEY_I = 0x17
35
+ KEY_O = 0x18
36
+ KEY_P = 0x19
37
+ LEFT_BRACKET = 0x1A
38
+ RIGHT_BRACKET = 0x1B
39
+ ENTER = 0x1C
40
+ LEFT_CTRL = 0x1D
41
+ KEY_A = 0x1E
42
+ KEY_S = 0x1F
43
+ KEY_D = 0x20
44
+ KEY_F = 0x21
45
+ KEY_G = 0x22
46
+ KEY_H = 0x23
47
+ KEY_J = 0x24
48
+ KEY_K = 0x25
49
+ KEY_L = 0x26
50
+ SEMICOLON = 0x27
51
+ QUOTE = 0x28
52
+ BACKTICK = 0x29
53
+ LEFT_SHIFT = 0x2A
54
+ BACKSLASH = 0x2B
55
+ KEY_Z = 0x2C
56
+ KEY_X = 0x2D
57
+ KEY_C = 0x2E
58
+ KEY_V = 0x2F
59
+ KEY_B = 0x30
60
+ KEY_N = 0x31
61
+ KEY_M = 0x32
62
+ COMMA = 0x33
63
+ PERIOD = 0x34
64
+ SLASH = 0x35
65
+ RIGHT_SHIFT = 0x36
66
+ KEYPAD_STAR = 0x37
67
+ LEFT_ALT = 0x38
68
+ SPACE = 0x39
69
+ CAPS_LOCK = 0x3A
70
+ F1 = 0x3B
71
+ F2 = 0x3C
72
+ F3 = 0x3D
73
+ F4 = 0x3E
74
+ F5 = 0x3F
75
+ F6 = 0x40
76
+ F7 = 0x41
77
+ F8 = 0x42
78
+ F9 = 0x43
79
+ F10 = 0x44
80
+ F11 = 0x57
81
+ F12 = 0x58
82
+ UP = 0x48
83
+ DOWN = 0x50
84
+ LEFT = 0x4B
85
+ RIGHT = 0x4D
86
+
87
+
88
+ # Scancode to character mapping (US layout)
89
+ SCANCODE_TO_CHAR: Dict[int, str] = {
90
+ 0x02: '1', 0x03: '2', 0x04: '3', 0x05: '4', 0x06: '5',
91
+ 0x07: '6', 0x08: '7', 0x09: '8', 0x0A: '9', 0x0B: '0',
92
+ 0x0C: '-', 0x0D: '=',
93
+ 0x10: 'q', 0x11: 'w', 0x12: 'e', 0x13: 'r', 0x14: 't',
94
+ 0x15: 'y', 0x16: 'u', 0x17: 'i', 0x18: 'o', 0x19: 'p',
95
+ 0x1A: '[', 0x1B: ']',
96
+ 0x1E: 'a', 0x1F: 's', 0x20: 'd', 0x21: 'f', 0x22: 'g',
97
+ 0x23: 'h', 0x24: 'j', 0x25: 'k', 0x26: 'l',
98
+ 0x27: ';', 0x28: "'", 0x29: '`',
99
+ 0x2B: '\\',
100
+ 0x2C: 'z', 0x2D: 'x', 0x2E: 'c', 0x2F: 'v', 0x30: 'b',
101
+ 0x31: 'n', 0x32: 'm',
102
+ 0x33: ',', 0x34: '.', 0x35: '/',
103
+ 0x39: ' ',
104
+ }
105
+
106
+ SCANCODE_TO_CHAR_SHIFT: Dict[int, str] = {
107
+ 0x02: '!', 0x03: '@', 0x04: '#', 0x05: '$', 0x06: '%',
108
+ 0x07: '^', 0x08: '&', 0x09: '*', 0x0A: '(', 0x0B: ')',
109
+ 0x0C: '_', 0x0D: '+',
110
+ 0x10: 'Q', 0x11: 'W', 0x12: 'E', 0x13: 'R', 0x14: 'T',
111
+ 0x15: 'Y', 0x16: 'U', 0x17: 'I', 0x18: 'O', 0x19: 'P',
112
+ 0x1A: '{', 0x1B: '}',
113
+ 0x1E: 'A', 0x1F: 'S', 0x20: 'D', 0x21: 'F', 0x22: 'G',
114
+ 0x23: 'H', 0x24: 'J', 0x25: 'K', 0x26: 'L',
115
+ 0x27: ':', 0x28: '"', 0x29: '~',
116
+ 0x2B: '|',
117
+ 0x2C: 'Z', 0x2D: 'X', 0x2E: 'C', 0x2F: 'V', 0x30: 'B',
118
+ 0x31: 'N', 0x32: 'M',
119
+ 0x33: '<', 0x34: '>', 0x35: '?',
120
+ 0x39: ' ',
121
+ }
122
+
123
+
124
+ @dataclass
125
+ class KeyEvent:
126
+ """Represents a keyboard event"""
127
+ scancode: int
128
+ char: str
129
+ pressed: bool # True for key down, False for key up
130
+ shift: bool
131
+ ctrl: bool
132
+ alt: bool
133
+
134
+
135
+ class Keyboard:
136
+ """
137
+ PS/2 Keyboard Driver.
138
+
139
+ Provides methods to read keyboard input.
140
+
141
+ Example:
142
+ @kernel.on_keypress
143
+ def handle_key(key):
144
+ Screen.print(f"You pressed: {key.char}")
145
+ """
146
+
147
+ _handlers: list = []
148
+ _buffer: list = []
149
+ _shift_pressed: bool = False
150
+ _ctrl_pressed: bool = False
151
+ _alt_pressed: bool = False
152
+ _operations: list = []
153
+
154
+ @classmethod
155
+ def read_key(cls) -> Optional[KeyEvent]:
156
+ """
157
+ Read a single key from the keyboard buffer.
158
+
159
+ Returns:
160
+ KeyEvent if a key is available, None otherwise.
161
+
162
+ Example:
163
+ key = Keyboard.read_key()
164
+ if key:
165
+ Screen.print(key.char)
166
+ """
167
+ cls._operations.append({"type": "read_key"})
168
+ if cls._buffer:
169
+ return cls._buffer.pop(0)
170
+ return None
171
+
172
+ @classmethod
173
+ def read_char(cls) -> Optional[str]:
174
+ """
175
+ Read a single character from keyboard.
176
+
177
+ Returns:
178
+ Character string if available, None otherwise.
179
+
180
+ Example:
181
+ char = Keyboard.read_char()
182
+ """
183
+ cls._operations.append({"type": "read_char"})
184
+ key = cls.read_key()
185
+ if key and key.char:
186
+ return key.char
187
+ return None
188
+
189
+ @classmethod
190
+ def read_line(cls) -> str:
191
+ """
192
+ Read a line of input (until Enter is pressed).
193
+
194
+ Returns:
195
+ The input string.
196
+
197
+ Example:
198
+ name = Keyboard.read_line()
199
+ Screen.print(f"Hello, {name}!")
200
+ """
201
+ cls._operations.append({"type": "read_line"})
202
+ return "" # Placeholder - actual implementation in ASM
203
+
204
+ @classmethod
205
+ def wait_key(cls) -> KeyEvent:
206
+ """
207
+ Wait for a key press (blocking).
208
+
209
+ Returns:
210
+ The KeyEvent when a key is pressed.
211
+
212
+ Example:
213
+ Screen.print("Press any key...")
214
+ key = Keyboard.wait_key()
215
+ """
216
+ cls._operations.append({"type": "wait_key"})
217
+ return KeyEvent(0, '', True, False, False, False)
218
+
219
+ @classmethod
220
+ def is_key_pressed(cls, key: KeyCode) -> bool:
221
+ """
222
+ Check if a specific key is currently pressed.
223
+
224
+ Args:
225
+ key: The KeyCode to check
226
+
227
+ Returns:
228
+ True if the key is pressed.
229
+
230
+ Example:
231
+ if Keyboard.is_key_pressed(KeyCode.SPACE):
232
+ Screen.print("Space is pressed!")
233
+ """
234
+ cls._operations.append({
235
+ "type": "is_key_pressed",
236
+ "keycode": key.value,
237
+ })
238
+ return False
239
+
240
+ @classmethod
241
+ def is_shift_pressed(cls) -> bool:
242
+ """Check if Shift key is pressed."""
243
+ return cls._shift_pressed
244
+
245
+ @classmethod
246
+ def is_ctrl_pressed(cls) -> bool:
247
+ """Check if Ctrl key is pressed."""
248
+ return cls._ctrl_pressed
249
+
250
+ @classmethod
251
+ def is_alt_pressed(cls) -> bool:
252
+ """Check if Alt key is pressed."""
253
+ return cls._alt_pressed
254
+
255
+ @classmethod
256
+ def clear_buffer(cls) -> None:
257
+ """Clear the keyboard buffer."""
258
+ cls._buffer.clear()
259
+ cls._operations.append({"type": "clear_buffer"})
260
+
261
+ @classmethod
262
+ def set_repeat_rate(cls, delay_ms: int = 500, rate_cps: int = 10) -> None:
263
+ """
264
+ Set keyboard repeat rate.
265
+
266
+ Args:
267
+ delay_ms: Delay before repeat starts (ms)
268
+ rate_cps: Characters per second when repeating
269
+ """
270
+ cls._operations.append({
271
+ "type": "set_repeat_rate",
272
+ "delay": delay_ms,
273
+ "rate": rate_cps,
274
+ })
275
+
276
+ @classmethod
277
+ def _process_scancode(cls, scancode: int) -> Optional[KeyEvent]:
278
+ """Process a raw scancode (used internally)."""
279
+ pressed = (scancode & 0x80) == 0
280
+ code = scancode & 0x7F
281
+
282
+ # Handle modifier keys
283
+ if code == KeyCode.LEFT_SHIFT.value or code == KeyCode.RIGHT_SHIFT.value:
284
+ cls._shift_pressed = pressed
285
+ return None
286
+ elif code == KeyCode.LEFT_CTRL.value:
287
+ cls._ctrl_pressed = pressed
288
+ return None
289
+ elif code == KeyCode.LEFT_ALT.value:
290
+ cls._alt_pressed = pressed
291
+ return None
292
+
293
+ # Get character
294
+ char_map = SCANCODE_TO_CHAR_SHIFT if cls._shift_pressed else SCANCODE_TO_CHAR
295
+ char = char_map.get(code, '')
296
+
297
+ return KeyEvent(
298
+ scancode=code,
299
+ char=char,
300
+ pressed=pressed,
301
+ shift=cls._shift_pressed,
302
+ ctrl=cls._ctrl_pressed,
303
+ alt=cls._alt_pressed,
304
+ )
305
+
306
+ @classmethod
307
+ def _reset(cls) -> None:
308
+ """Reset keyboard state (used internally)."""
309
+ cls._handlers = []
310
+ cls._buffer = []
311
+ cls._shift_pressed = False
312
+ cls._ctrl_pressed = False
313
+ cls._alt_pressed = False
314
+ cls._operations = []
315
+
316
+ @classmethod
317
+ def _get_operations(cls) -> list:
318
+ """Get all recorded operations (used by compiler)."""
319
+ return cls._operations.copy()
pyos/drivers/screen.py ADDED
@@ -0,0 +1,310 @@
1
+ """
2
+ pyOS Screen Driver - VGA Text Mode
3
+ """
4
+
5
+ from typing import Optional, Tuple
6
+ from enum import Enum
7
+ from dataclasses import dataclass
8
+
9
+
10
+ class Color(Enum):
11
+ """VGA Text Mode Colors"""
12
+ BLACK = 0
13
+ BLUE = 1
14
+ GREEN = 2
15
+ CYAN = 3
16
+ RED = 4
17
+ MAGENTA = 5
18
+ BROWN = 6
19
+ LIGHT_GRAY = 7
20
+ DARK_GRAY = 8
21
+ LIGHT_BLUE = 9
22
+ LIGHT_GREEN = 10
23
+ LIGHT_CYAN = 11
24
+ LIGHT_RED = 12
25
+ LIGHT_MAGENTA = 13
26
+ YELLOW = 14
27
+ WHITE = 15
28
+
29
+
30
+ # Color name mapping
31
+ COLOR_NAMES = {
32
+ "black": Color.BLACK,
33
+ "blue": Color.BLUE,
34
+ "green": Color.GREEN,
35
+ "cyan": Color.CYAN,
36
+ "red": Color.RED,
37
+ "magenta": Color.MAGENTA,
38
+ "brown": Color.BROWN,
39
+ "light_gray": Color.LIGHT_GRAY,
40
+ "gray": Color.LIGHT_GRAY,
41
+ "dark_gray": Color.DARK_GRAY,
42
+ "light_blue": Color.LIGHT_BLUE,
43
+ "light_green": Color.LIGHT_GREEN,
44
+ "light_cyan": Color.LIGHT_CYAN,
45
+ "light_red": Color.LIGHT_RED,
46
+ "light_magenta": Color.LIGHT_MAGENTA,
47
+ "yellow": Color.YELLOW,
48
+ "white": Color.WHITE,
49
+ }
50
+
51
+
52
+ @dataclass
53
+ class ScreenConfig:
54
+ """Screen configuration"""
55
+ width: int = 80
56
+ height: int = 25
57
+ vga_address: int = 0xB8000
58
+
59
+
60
+ class Screen:
61
+ """
62
+ VGA Text Mode Screen Driver.
63
+
64
+ Provides methods to write text to the screen with colors.
65
+ All methods are class methods for easy access.
66
+
67
+ Example:
68
+ Screen.clear()
69
+ Screen.set_color("green", "black")
70
+ Screen.print("Hello World!")
71
+ Screen.print_at("Position text", row=5, col=10)
72
+ """
73
+
74
+ _config = ScreenConfig()
75
+ _cursor_row: int = 0
76
+ _cursor_col: int = 0
77
+ _foreground: Color = Color.WHITE
78
+ _background: Color = Color.BLACK
79
+
80
+ # Internal buffer for compilation
81
+ _operations: list = []
82
+
83
+ @classmethod
84
+ def clear(cls) -> None:
85
+ """
86
+ Clear the entire screen.
87
+
88
+ Example:
89
+ Screen.clear()
90
+ """
91
+ cls._cursor_row = 0
92
+ cls._cursor_col = 0
93
+ cls._operations.append({
94
+ "type": "clear",
95
+ })
96
+
97
+ @classmethod
98
+ def print(
99
+ cls,
100
+ text: str,
101
+ row: Optional[int] = None,
102
+ col: Optional[int] = None,
103
+ color: Optional[str] = None,
104
+ background: Optional[str] = None,
105
+ ) -> None:
106
+ """
107
+ Print text to the screen.
108
+
109
+ Args:
110
+ text: The text to print
111
+ row: Row position (optional, uses cursor if not specified)
112
+ col: Column position (optional, uses cursor if not specified)
113
+ color: Foreground color name (optional)
114
+ background: Background color name (optional)
115
+
116
+ Example:
117
+ Screen.print("Hello World!")
118
+ Screen.print("Colored text", color="green")
119
+ Screen.print("At position", row=5, col=10)
120
+ """
121
+ if row is not None:
122
+ cls._cursor_row = row
123
+ if col is not None:
124
+ cls._cursor_col = col
125
+
126
+ fg = cls._foreground
127
+ bg = cls._background
128
+
129
+ if color is not None:
130
+ fg = COLOR_NAMES.get(color.lower(), cls._foreground)
131
+ if background is not None:
132
+ bg = COLOR_NAMES.get(background.lower(), cls._background)
133
+
134
+ cls._operations.append({
135
+ "type": "print",
136
+ "text": text,
137
+ "row": cls._cursor_row,
138
+ "col": cls._cursor_col,
139
+ "foreground": fg.value,
140
+ "background": bg.value,
141
+ })
142
+
143
+ # Move cursor to next line
144
+ cls._cursor_row += 1
145
+ cls._cursor_col = 0
146
+
147
+ @classmethod
148
+ def print_at(
149
+ cls,
150
+ text: str,
151
+ row: int,
152
+ col: int,
153
+ color: Optional[str] = None,
154
+ background: Optional[str] = None,
155
+ ) -> None:
156
+ """
157
+ Print text at a specific position.
158
+
159
+ Args:
160
+ text: The text to print
161
+ row: Row position (0-24)
162
+ col: Column position (0-79)
163
+ color: Foreground color name (optional)
164
+ background: Background color name (optional)
165
+
166
+ Example:
167
+ Screen.print_at("Hello", row=10, col=35)
168
+ """
169
+ cls.print(text, row=row, col=col, color=color, background=background)
170
+
171
+ @classmethod
172
+ def print_char(
173
+ cls,
174
+ char: str,
175
+ row: int,
176
+ col: int,
177
+ color: Optional[str] = None,
178
+ background: Optional[str] = None,
179
+ ) -> None:
180
+ """
181
+ Print a single character at a specific position.
182
+
183
+ Args:
184
+ char: Single character to print
185
+ row: Row position
186
+ col: Column position
187
+ color: Foreground color (optional)
188
+ background: Background color (optional)
189
+ """
190
+ cls._operations.append({
191
+ "type": "print_char",
192
+ "char": char[0] if char else ' ',
193
+ "row": row,
194
+ "col": col,
195
+ "foreground": COLOR_NAMES.get(color, cls._foreground).value if color else cls._foreground.value,
196
+ "background": COLOR_NAMES.get(background, cls._background).value if background else cls._background.value,
197
+ })
198
+
199
+ @classmethod
200
+ def set_color(cls, foreground: str, background: str = "black") -> None:
201
+ """
202
+ Set the default text colors.
203
+
204
+ Args:
205
+ foreground: Foreground color name
206
+ background: Background color name (default: black)
207
+
208
+ Example:
209
+ Screen.set_color("green", "black")
210
+ Screen.set_color("white", "blue")
211
+ """
212
+ cls._foreground = COLOR_NAMES.get(foreground.lower(), Color.WHITE)
213
+ cls._background = COLOR_NAMES.get(background.lower(), Color.BLACK)
214
+
215
+ cls._operations.append({
216
+ "type": "set_color",
217
+ "foreground": cls._foreground.value,
218
+ "background": cls._background.value,
219
+ })
220
+
221
+ @classmethod
222
+ def set_cursor(cls, row: int, col: int) -> None:
223
+ """
224
+ Set the cursor position.
225
+
226
+ Args:
227
+ row: Row position (0-24)
228
+ col: Column position (0-79)
229
+
230
+ Example:
231
+ Screen.set_cursor(10, 40)
232
+ """
233
+ cls._cursor_row = max(0, min(row, cls._config.height - 1))
234
+ cls._cursor_col = max(0, min(col, cls._config.width - 1))
235
+
236
+ cls._operations.append({
237
+ "type": "set_cursor",
238
+ "row": cls._cursor_row,
239
+ "col": cls._cursor_col,
240
+ })
241
+
242
+ @classmethod
243
+ def get_cursor(cls) -> Tuple[int, int]:
244
+ """
245
+ Get the current cursor position.
246
+
247
+ Returns:
248
+ Tuple of (row, col)
249
+ """
250
+ return (cls._cursor_row, cls._cursor_col)
251
+
252
+ @classmethod
253
+ def scroll_up(cls, lines: int = 1) -> None:
254
+ """
255
+ Scroll the screen up by a number of lines.
256
+
257
+ Args:
258
+ lines: Number of lines to scroll (default: 1)
259
+ """
260
+ cls._operations.append({
261
+ "type": "scroll_up",
262
+ "lines": lines,
263
+ })
264
+
265
+ @classmethod
266
+ def scroll_down(cls, lines: int = 1) -> None:
267
+ """
268
+ Scroll the screen down by a number of lines.
269
+
270
+ Args:
271
+ lines: Number of lines to scroll (default: 1)
272
+ """
273
+ cls._operations.append({
274
+ "type": "scroll_down",
275
+ "lines": lines,
276
+ })
277
+
278
+ @classmethod
279
+ def enable_cursor(cls) -> None:
280
+ """Enable the hardware cursor."""
281
+ cls._operations.append({"type": "enable_cursor"})
282
+
283
+ @classmethod
284
+ def disable_cursor(cls) -> None:
285
+ """Disable the hardware cursor."""
286
+ cls._operations.append({"type": "disable_cursor"})
287
+
288
+ @classmethod
289
+ def get_width(cls) -> int:
290
+ """Get screen width in characters."""
291
+ return cls._config.width
292
+
293
+ @classmethod
294
+ def get_height(cls) -> int:
295
+ """Get screen height in characters."""
296
+ return cls._config.height
297
+
298
+ @classmethod
299
+ def _reset(cls) -> None:
300
+ """Reset screen state (used internally)."""
301
+ cls._cursor_row = 0
302
+ cls._cursor_col = 0
303
+ cls._foreground = Color.WHITE
304
+ cls._background = Color.BLACK
305
+ cls._operations = []
306
+
307
+ @classmethod
308
+ def _get_operations(cls) -> list:
309
+ """Get all recorded operations (used by compiler)."""
310
+ return cls._operations.copy()