├── LICENSE
├── README.md
├── aedb
├── bin
└── aed.bin
├── makefile
├── old
└── aed.s
└── src
├── char_buffer.c
├── char_buffer.h
├── cmd_ops.c
├── cmd_ops.h
├── conv.c
├── conv.h
├── editor.c
├── editor.h
├── line_buffer.c
├── line_buffer.h
├── main.c
├── screen.c
├── screen.h
├── text_buffer.c
├── text_buffer.h
├── user_input.c
├── user_input.h
└── vkey.h
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AED: Another Text Editor
2 |
3 | AED is a text editor for the Agon platform (Agon light, Agon light2, Agon Origins, Console8).
4 | It's screen navigation was inspired by various text editors I have used (notepad / nano / kate / vim) and
5 | that inspiration drove the design and choices of navigation keys.
6 |
7 | Currently it is limited to reading and writing files up to 256KB long with up to 8k lines.
8 |
9 | The editor can work in any Agon supported resolution and will use whatever color scheme you've configured
10 | your Agon.
11 |
12 | `NOTE: Since release v0.13.0, VDP 1.04 or above is required.`
13 |
14 | # Installation
15 |
16 | Copy the `aed.bin` file to your sdcard's `/mos` directory, You should now be able to run it just typing `aed` at the command line.
17 |
18 | > NOTE: The editor uses most of the memory available, so do not start it if you are in BBCBasic.
19 |
20 | # Running the editor.
21 | If you run it just as `aed` it will start the editor using `/aed.txt` as its backing file. If the file can't be created,
22 | it will exit with the message `Quit`. If the file already exists, it will read it into the buffer and display it on the editor screen.
23 |
24 | You can specify the file at startup by typing `aed file.name` and it will try to create it, exiting with `Quit` if it can't.
25 | If the file already exists, it will read it into the buffer and display it on the editor screen.
26 |
27 | # File operations
28 | `CTRL+S` will save the current text buffer to a file. If no file name was provided on startup, a prompt for the file name is shown.
29 | `CTRL+ALT+S` will always show a prompt for the file name before saving.
30 |
31 | # Navigation and shortcuts.
32 | You navigate using the `LEFT, RIGHT, UP, DOWN` arrow keys to move the cursor one character at a time. The cursor will wrap around lines if you
33 | try to move past the end or beginning. You can also use `CTRL+LEFT` and `CTRL+RIGHT` to navigate between white spaces (words) for
34 | faster movement.
35 |
36 | Use `PAGE_UP / PAGE_DOWN` to move a page of text at a time.
37 |
38 | `DELETE` and `BACKSPACE` keys work as expected, removing characters under the cursor (`DELETE`) and to the left of the cursor (`BACKSPACE`).
39 | If at the end of the line, `DELETE` will merge the next line with the current one.
40 |
41 | You can press `CTRL+D` or `CTRL+DELETE` to delete a whole line.
42 |
43 | `CTRL+Q` will save the buffer to the specified file on startup (or `/aed.txt` of none was specified) and exit the editor.
44 | If no file was specified on startup, it will prompt for a file name to save the text buffer.
45 |
46 | `CTRL+ALT+C` will show the colour picker at the bottom of the screen. Use `UP/DOWN` to select the foreground color and `LEFT/RIGHT` to
47 | select the background color.
48 |
49 | # Road to v1.0
50 | The following features will be implemented before releasing v1.0 of the editor:
51 |
52 | - [x] ~~BACKSPACE merges current line with previous when pressed at the beginning of the line.~~
53 | - [x] ~~Shortcut to change foreground and background colors.~~
54 | - [x] ~~`PAGE-UP` and `PAGE-DOWN` support.~~
55 | - [x] ~~Shortcut for saving the current buffer without quiting.~~
56 | - [ ] File selection while in the editor.
57 | - [ ] Copy-cut-paste.
58 | - [ ] Find.
59 |
60 | ## Roadmap after v1.0
61 |
62 | - [ ] Undo / Redo.
63 | - [ ] Native tabs / tab-size / tab-to-space.
64 | - [ ] Syntax highlighting for BBCBasic and assembly files.
65 | - [ ] Unlimted file size support.
66 | - [ ] Console8 mouse support (need to get one).
67 |
--------------------------------------------------------------------------------
/aedb:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | make V=1 && \
3 | \ls -l ~/fab-agon-emulator/sdcard/mos/aed.bin && \
4 | cp bin/aed.bin ~/fab-agon-emulator/sdcard/mos && \
5 | \ls -l ~/fab-agon-emulator/sdcard/mos/aed.bin
6 |
--------------------------------------------------------------------------------
/bin/aed.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/avalonbits/aed/92857c34759543605f41372eed644745141fac40/bin/aed.bin
--------------------------------------------------------------------------------
/makefile:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2023 Igor Cananea
2 | # Author: Igor Cananea
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU General Public License as published by
6 | # the Free Software Foundation, either version 3 of the License, or
7 | # (at your option) any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public License
15 | # along with this program. If not, see .
16 |
17 | # ----------------------------
18 | # Makefile Options
19 | # ----------------------------
20 |
21 | NAME = aed
22 | ICON = icon.png
23 | DESCRIPTION = "Agon text editor."
24 | COMPRESSED = NO
25 | ARCHIVED = NO
26 | INIT_LOC = 0B0000
27 | BSSHEAP_LOW = 040000
28 | BSSHEAP_HIGH = 0A7FFF
29 | STACK_HIGH = 0AFFFF
30 |
31 | CFLAGS = -Werror -Wall -Wextra -Oz
32 | CXXFLAGS = -Werror -Wall -Wextra -Oz
33 |
34 | # ----------------------------
35 |
36 | include $(shell cedev-config --makefile)
37 |
--------------------------------------------------------------------------------
/old/aed.s:
--------------------------------------------------------------------------------
1 | ; Copyright (C) 2023 Igor Cananea
2 | ; Author: Igor Cananea
3 | ;
4 | ; This program is free software: you can redistribute it and/or modify
5 | ; it under the terms of the GNU General Public License as published by
6 | ; the Free Software Foundation, either version 3 of the License, or
7 | ; (at your option) any later version.
8 | ;
9 | ; This program is distributed in the hope that it will be useful,
10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | ; GNU General Public License for more details.
13 | ;
14 | ; You should have received a copy of the GNU General Public License
15 | ; along with this program. If not, see .
16 |
17 | .assume ADL = 1
18 | .org $A0000
19 |
20 | jp _start
21 |
22 | _debug01: .ds 3,0
23 | _debug02: .ds 3,0
24 |
25 | .align 64
26 |
27 | .db "MOS"
28 | .db 00h
29 | .db 01h
30 |
31 | ; Program entry point.
32 | _start:
33 | ; Initialize screen and text buffer.
34 | call _s_init
35 | call tb_init
36 |
37 | @keyloop:
38 | call _process_input
39 |
40 | ; If returns non-zero, we can exit the editor.
41 | or a
42 | jr Z, @keyloop
43 |
44 | @done:
45 | call _s_done
46 | ld hl, 0
47 | ret
48 |
49 |
50 | ; PROCESS_INPUT
51 | ; Returns:
52 | ; - A: Status code
53 | ; * 0: Success
54 | ; * 1: Exit program
55 | _process_input:
56 | ; Get sysvars
57 | ld a, 08h
58 | rst.lil 08h
59 |
60 | ; Call getkey
61 | ld a, 00h
62 | rst.lil 08h
63 |
64 | ; First, check if we have the CTRL key pressed.
65 | bit 0, (ix+06h)
66 |
67 | ; If the bit isn't set, then we can continue processing as normal.
68 | jp Z, @normal
69 |
70 | ; We have CTRL pressed, get the scancode for the pressed key.
71 | ld a, (ix+17h)
72 | ; CTRL+q or CTRL+Q exists the editor.
73 | cp 26h
74 | jp Z, @exit
75 | cp 40h
76 | jp Z, @exit
77 |
78 | ; No more CTRL processing.
79 | ld a,0
80 | ret
81 |
82 |
83 | @normal:
84 | ; 7F and anything below 32 is a control char.
85 | cp 32
86 | jp M, @control
87 | cp 7Fh
88 | jp Z, @control
89 |
90 | ; Printable character.
91 | ; Add the character to the screen, replcaing the cursor.
92 | call _s_putc
93 |
94 | ; Now add it to the text buffer.
95 | call tb_putc
96 | jr @done
97 |
98 | @control:
99 | ; Get keycode from sysvar. It is on offset 17h and is 1 byte.
100 | ld a, (ix+17h)
101 |
102 | ; Left arrow.
103 | cp 9Ah
104 | jr Z, @m_left
105 |
106 | ; Right arrow.
107 | cp 9Ch
108 | jr Z, @m_right
109 |
110 | ; Delete.
111 | cp 82h
112 | jr Z, @m_del
113 |
114 | ; Backspace.
115 | cp 84h
116 | jr Z, @m_bkspace
117 |
118 | ; Home
119 | cp 85h
120 | jr Z, @m_home
121 | cp 86h
122 | jr Z, @m_home
123 |
124 | ; Ignore other keys
125 | ld a,0
126 | ret
127 |
128 | @m_left:
129 | ; First replace the cursor with the character under it.
130 | call _replace_cur
131 |
132 | ; Now move the cursor to the left in the buffer.
133 | call tb_left
134 | ld a, b
135 | or a
136 | jr NZ, @done
137 |
138 | ; Move the cursor to left on VDP.
139 | ld a, 8
140 | rst.lil 10h
141 |
142 | ; Dec curx
143 | ld a, (_s_curx)
144 | dec a
145 | ld (_s_curx), a
146 | jr @done
147 |
148 | @m_right:
149 | call _replace_cur
150 |
151 | call tb_right
152 | ld a, b
153 | or a
154 | jr NZ, @done
155 |
156 | ; Move the curosr right on VDP.
157 | ld a, 9
158 | rst.lil 10h
159 |
160 | ; Inc curx
161 | ld a, (_s_curx)
162 | inc a
163 | ld (_s_curx), a
164 | jr @done
165 |
166 | @m_del:
167 | call _replace_cur
168 | call tb_delete
169 | call _s_delete
170 | jr @done
171 |
172 | @m_bkspace:
173 | call _s_bkspace
174 | call tb_bkspace
175 | jr @done
176 |
177 | @m_home:
178 | call _replace_cur
179 | call tb_home
180 | xor a
181 | ld (_s_curx), a
182 | call _s_cback
183 | jr @done
184 |
185 | @done:
186 | ; The cursor is no longer on the screen. Bring it back.
187 | call tb_getc
188 | or a
189 | jr NZ, @cursor
190 | ld a, 32
191 |
192 | @cursor:
193 | call _s_scur
194 | ld a, 0
195 | ret
196 |
197 | @exit:
198 | ld a, 1
199 | ret
200 |
201 | ; REPLACE_CUR: Print the caracter under the cursor, replacing it on screen.
202 | ; Preserves:
203 | ; - AF.
204 | _replace_cur:
205 | ; Get the character currently under the cursor
206 | push af
207 | call tb_getc
208 |
209 | ; If 0, there is no character, just use space.
210 | or a
211 | jr Z, @space
212 |
213 | ; If its a carriage return, we are at end of line, so use space.
214 | cp '\r'
215 | jr Z, @space
216 |
217 | ; Anyhing else, use it.
218 | jr @replace
219 |
220 | @space:
221 |
222 | ; We have no printable character under the cursor. Print space.
223 | ld a, 32
224 |
225 | @replace:
226 | call _s_hcur
227 |
228 | ; We are done.
229 | pop af
230 | ret
231 |
232 |
233 |
234 | _enable_cursor: .db 23,1,1
235 | _scroll_up: .db 23,7,0,3,8
236 | _str: .db "Hello, world!",0
237 | _str_end:
238 |
239 |
240 | ;**** SCREEN BUFFER ****
241 |
242 | ; Cursor is relative to the number of chars.
243 | ; Highest supported resolution is 1280x720, which with an 8x8 font means
244 | ; 160x90 chars, fitting each dimension in a single byte.
245 | _s_curx: .db 0
246 | _s_cury: .db 0
247 |
248 | ; Max width and height in chars. This should be set on init and on every
249 | ; resolution change.
250 | _s_maxw: .db 0
251 | _s_maxh: .db 0
252 | _s_eos: .db 0
253 |
254 | ; Screen foreground and background colors.
255 | _s_fg: .db 15
256 | _s_bg: .db 0
257 |
258 | ; VDP init string:
259 | ; - Disables cursor.
260 | ; - Sets foreground and background colors.
261 | ; - Clears screen
262 | ; - Moves cursor home.
263 | _init_vdp: .db 23,1,0,17,15,17,128,12,30
264 | _init_vdp_end:
265 |
266 |
267 | ; S_INIT: Initializes the screen for the editor.
268 | ; Preserves:
269 | ; - AF, BC, HL.
270 | _s_init:
271 | push af
272 | push bc
273 | push hl
274 |
275 | ; Init VDP for our text editor
276 | ld hl, _init_vdp
277 | ld bc, _init_vdp_end-_init_vdp
278 | rst.lil 18h
279 |
280 | ; Get sysvars
281 | ld a, 08h
282 | rst.lil 08h
283 |
284 | ; Store #cols.
285 | ld a, (ix+13h)
286 | dec a
287 | ld (_s_maxw), a
288 |
289 | ; Store #rows.
290 | ld a, (ix+14h)
291 | dec a
292 | ld (_s_maxh), a
293 |
294 | ; Init cursor.
295 | ld a, 0
296 | ld (_s_curx), a
297 | ld (_s_cury), a
298 | ld (_s_eos), a
299 |
300 | ; Show the cursor on screen
301 | ld a, 32
302 | call _s_scur
303 |
304 | ; Init is done.
305 | pop hl
306 | pop bc
307 | pop af
308 | ret
309 |
310 |
311 | _vdp_tabxy: .db 31,0,0
312 |
313 | ; _S_CBACK: Moves cursor to _s_curx,_s_cury
314 | ; Preserves:
315 | ; - AF, BC, HL
316 | _s_cback:
317 | push af
318 | push bc
319 | push hl
320 | push ix
321 |
322 | ld ix, _vdp_tabxy
323 | ld a, (_s_curx)
324 | ld (ix+1), a
325 | ld a, (_s_cury)
326 | ld (ix+2), a
327 | ld hl, _vdp_tabxy
328 | ld bc, 3
329 | rst.lil 18h
330 |
331 | pop ix
332 | pop hl
333 | pop bc
334 | pop af
335 | ret
336 |
337 | ; _S_MCUR: Moves cursor to B,C
338 | _s_mcur:
339 | push af
340 | push hl
341 | push ix
342 |
343 | ld ix, _vdp_tabxy
344 | ld (ix+1), b
345 | ld (ix+2), c
346 | ld hl, _vdp_tabxy
347 | ld bc, 3
348 | rst.lil 18h
349 |
350 | pop ix
351 | pop hl
352 | pop af
353 | ret
354 |
355 | ; S_HCUR: Hides the cursor
356 | ; - A: Character to use as cursor.
357 | ; Preserves
358 | ; - AF, BC, HL.
359 | _s_hcur:
360 | push hl
361 | push bc
362 | push af
363 |
364 | ; First set the correct colors.
365 | ld hl, _s_fg
366 | ld c, (hl)
367 | ld hl, _s_bg
368 | ld b, (hl)
369 | call _set_color
370 |
371 | ; Now print the cursor
372 | rst.lil 10h
373 |
374 | ; Because printing the cursor moves it on vdp, we need to bring it
375 | ; back.
376 | call _s_cback
377 |
378 | ; We are done
379 | pop af
380 | pop bc
381 | pop hl
382 | ret
383 |
384 | ; S_SCUR: Shows the cursor on screen.
385 | ; - A: Character to use as cursor.
386 | ; Preserves
387 | ; - AF, BC, HL.
388 | _s_scur:
389 | push hl
390 | push bc
391 | push af
392 |
393 | ; First reverse fg/bg.
394 | ld hl, _s_bg
395 | ld c, (hl)
396 | ld hl, _s_fg
397 | ld b, (hl)
398 | call _set_color
399 |
400 | ; Now print the cursor.
401 | rst.lil 10h
402 |
403 |
404 | ; First reverse fg/bg.
405 | ; Because printing the cursor moves it on vdp, we need to bring it
406 | ; back.
407 | call _s_cback
408 | @done:
409 | ; Now set the correct colors back.
410 | ld hl, _s_fg
411 | ld c, (hl)
412 | ld hl, _s_bg
413 | ld b, (hl)
414 | call _set_color
415 |
416 | ; We are done.
417 | pop af
418 | pop bc
419 | pop hl
420 | ret
421 |
422 | ; SET_COLOR: Sets the colors for the screen
423 | ; Args:
424 | ; - C: Forground color.
425 | ; - B: Backgrond color.
426 | ; Preserves:
427 | ; - AF.
428 | _set_color:
429 | push af
430 | ; Set foreground color.
431 | ld a, 17
432 | rst.lil 10h
433 | ld a, c
434 | rst.lil 10h
435 |
436 | ; Set background color.
437 | ld a, 17
438 | rst.lil 10h
439 | ld a, b
440 | add a, 128
441 | rst.lil 10h
442 |
443 | ; We are done.
444 | pop af
445 | ret
446 |
447 |
448 | ; S_PUTC: Prints a character on screen.
449 | ; - A: Character to print.
450 | ; Preserves:
451 | ; - AF, HL.
452 | _s_putc:
453 | push af
454 | push hl
455 |
456 | ; Adjust the view if needed.
457 | call _s_pview
458 | @print:
459 |
460 | ; Print the char on screen
461 | rst.lil 10h
462 |
463 |
464 | ; Inc curx and eos.
465 | ld a, (_s_curx)
466 | inc a
467 | ld (_s_curx), a
468 |
469 | ; Increment eos if we haven't filled the line.
470 | ld a, (_s_maxw)
471 | ld b, a
472 | ld a, (_s_eos)
473 | cp b
474 |
475 | ; If they are the same, we can' t increment.
476 | jr Z, @done
477 |
478 | ; Still have space on the line.
479 | inc a
480 | ld (_s_eos), a
481 |
482 | ; Signal we moved
483 | ld b, 0
484 |
485 | @done:
486 | pop hl
487 | pop af
488 | ret
489 |
490 | _s_offset: .db 0,0,0
491 |
492 | ; S_VIEW: Adjust the charactor view so that we can scroll the line.
493 | _s_pview:
494 | push af
495 | push de
496 | push bc
497 | push hl
498 |
499 | ; Given that this is only called right before putting a character on
500 | ; the screen, we want to make sure there is enough space for it.
501 | ; There are cases to handle:
502 | ; - Cursor at EOL:
503 | ; * Shift everyone to the left to create a space. No tail work.
504 | ; - Cursor somewhere in line:
505 | ; * Move everyone after the cursor to the right, removing char if
506 | ; needed.
507 |
508 | ; So first, let's figure out where we are.
509 | ld a, (_s_curx)
510 | ld b, a
511 | ld a, (_s_maxw)
512 | cp b
513 |
514 | ; If s_curx != s_maxw, then cursor is not at end of screen.
515 | jr NZ, @inline
516 |
517 | ; We are at end of screen.
518 | ; To shift we want to shift the line to the left. For that we define
519 | ; The current line as the region we want to shift, shift it and then
520 | ; redefine the region again.
521 | ld de, (_s_offset)
522 | inc de
523 | ld (_s_offset), de
524 |
525 | ; With this, HL points to start of string, BC has the string count.
526 | call tb_gethead
527 |
528 |
529 | @shift_left:
530 | inc hl
531 | dec de
532 |
533 | ; If we still have chars to offset, loop back.
534 | ld a, d
535 | or e
536 | jr NZ, @shift_left
537 |
538 |
539 | @print_left:
540 | ; So now, we move cursor to home
541 | ld b, 0
542 | ld a, (_s_cury)
543 | ld c,a
544 | call _s_mcur
545 |
546 | ; Print the string.
547 | ld bc, 0
548 | ld a, (_s_curx)
549 | dec a
550 | ld c, a
551 | rst.lil 18h
552 |
553 | ; Move cursor one position back
554 | ld a, (_s_curx)
555 | dec a
556 | ld (_s_curx), a
557 |
558 | ; Move cursor to curx,cury
559 | call _s_cback
560 |
561 | jr @done
562 |
563 |
564 | @inline:
565 | ; We have enough space. Then just adjust the tail.
566 | call tb_gettail
567 | ld a, b
568 | or c
569 |
570 | ; If we have no tail, we are done.
571 | jr Z, @done
572 |
573 | ; We have a tail. Move 1 space to the right,
574 | ld a, 9
575 | rst.lil 10h
576 |
577 | ; Get the current position.
578 | ld a, (_s_curx)
579 | ld e, a
580 |
581 | ; Print chars from tail until we are done or if we hit the end of the
582 | ; screen.
583 | @tail_print:
584 | ; Did we hit end of screen?
585 | ld a, (_s_maxw)
586 | cp e
587 | jr Z, @move_back
588 |
589 | ; Did we finish writing chars?
590 | ld a, b
591 | or c
592 | jr Z, @move_back
593 |
594 | ; Update counters.
595 | inc e
596 | dec bc
597 |
598 | ; Print char.
599 | ld a, (hl)
600 | inc hl
601 | push de
602 | push bc
603 | rst.lil 10h
604 | pop bc
605 | pop de
606 |
607 | ; Next char.
608 | jr @tail_print
609 |
610 |
611 | @move_back:
612 | ; Move the cursor back to _s_curx,_s_cury
613 | call _s_cback
614 |
615 |
616 | @done:
617 | pop hl
618 | pop bc
619 | pop de
620 | pop af
621 | ret
622 |
623 | ; S_BACKSPACE: Erase a char to the left of the cursor on screen.
624 | ; Preserves:
625 | ; - AF
626 | _s_bkspace:
627 | push af
628 |
629 | ; First check if we can move.
630 | ld a, (_s_curx)
631 | or a
632 | jr Z, @done
633 |
634 | ; Remove cursor from screen
635 | call _replace_cur
636 |
637 | ; Move cursor to the left.
638 | dec a
639 | ld (_s_curx), a
640 | call _s_cback
641 |
642 | ; Get the tail
643 | call tb_gettail
644 | ld a, b
645 | or c
646 | jr Z, @space
647 |
648 | ; We have tail, print it.
649 | rst.lil 18h
650 |
651 | @space:
652 | ; Print the space to remove the char from the screen.
653 | ld a, 32
654 | rst.lil 10h
655 |
656 | ; Bring the cursor back
657 | call _s_cback
658 |
659 | @done:
660 | pop af
661 | ret
662 |
663 |
664 | ; S_DELETE: Erase a char to the right of the cursor.
665 | ; Preserve:
666 | ; - AF
667 | _s_delete:
668 | push af
669 |
670 | ; Get the tail. If there are no chars, nothing to do.
671 | call tb_gettail
672 | ld a, b
673 | or c
674 | jr Z, @done
675 |
676 | ; We have chars and we have previously call tb_delete. So, we just
677 | ; need to print the tail + space char.
678 | push bc
679 | rst.lil 18h
680 | ld a, 32
681 | rst.lil 10h
682 |
683 | ; Now bring back the cursor to the correct position.
684 | ; Increment bc to account for the extra space print
685 | pop bc
686 | inc bc
687 | @loop:
688 | call _s_cback
689 | dec bc
690 | ld a, b
691 | or c
692 | jr NZ, @loop
693 |
694 | @done:
695 | pop af
696 | ret
697 |
698 | ;S_DONE: Restores the computer back to a usable state.
699 | _s_done:
700 | ; Clear screen.
701 | ld a, 12
702 | rst.lil 10h
703 |
704 | ; Bring cursor home.
705 | ld a, 30
706 | rst.lil 10h
707 |
708 | ; Renable cursor
709 | ld hl, _enable_cursor
710 | ld bc, 3
711 | rst.lil 18h
712 |
713 | ; Done
714 | ret
715 |
716 |
717 |
718 | ;**** TEXT BUFFER ****
719 |
720 | ; Gap Buffer for the text.
721 | _tb_start: .ds 3,0 ; Pointer to buf start.
722 | _tb_end: .ds 3,0 ; Pointer to buf end.
723 | _ccur: .ds 3,0 ; Pointer to cursor position in buf.
724 | _cend: .ds 3,0 ; Pointer to buffer suffix.
725 | _slptr: .ds 3,0 ; Pointer to start of current line.
726 | _elptr: .ds 3,0 ; Pointer to end of current line.
727 | _szptr: .ds 3,0 ; Pointer to line size.
728 |
729 | ; Line/Char position in buffer.
730 | _lpos: .ds 3,0
731 | _cpos: .ds 3,0
732 |
733 | ; Line count, init to 1.
734 | _lcount: .db 1,0,0
735 | ; Char count, init t0 0.
736 | _ccount: .ds 3,0
737 |
738 | ; TB_INIT: Initializes the text buffer.
739 | ; Preserves:
740 | ; - IX, HL, BC.
741 | tb_init:
742 | push hl
743 | push bc
744 |
745 | ; Set the start pointers for text buffer.
746 | ld hl, 40000h
747 | ld (_tb_start), hl
748 | ld (_szptr), hl
749 |
750 | ; Every line starts with 3 bytes that is the size counter.
751 | ld hl, 40003h
752 | ld (_ccur), hl
753 | ld (_slptr), hl
754 |
755 | ; Set the end pointers for text buffer.
756 | ld hl, A0000h
757 | ld (_tb_end), hl
758 | ld (_cend), hl
759 | ld (_elptr), hl
760 |
761 | ; We are done.
762 | pop bc
763 | pop hl
764 | ret
765 |
766 |
767 | ; TB_GETC: Returns the current char under the cursor.
768 | ; Returns:
769 | ; - A: The ASCII code of current char, 32 (space) if buffer is empty.
770 | ; Preserves:
771 | ; - DE, HL.
772 | tb_getc:
773 | ; Preserve registers.
774 | push de
775 | push hl
776 |
777 | ; If there are chars at end, then the cursor is covering a char.
778 | ld hl, (_elptr)
779 | ld de, (_cend)
780 | ld a,32
781 |
782 | scf
783 | ccf
784 | sbc hl,de
785 | jp Z, @done
786 |
787 | ; Cursor is covering a char. Return it.
788 | ld de, (_cend)
789 | ld a, (de)
790 |
791 | @done:
792 | pop hl
793 | pop de
794 | ret
795 |
796 |
797 | ; TB_LINE_NO: Returns the current line number.
798 | ; Returns:
799 | ; - BC: Line number.
800 | tb_line_no:
801 | ld bc, (_lpos)
802 | ret
803 |
804 |
805 |
806 | ; TB_CHAR_NO: Returns the current char number at current line.
807 | ; Returns:
808 | ; - BC: Char number.
809 | tb_char_no:
810 | ld bc, (_cpos)
811 | ret
812 |
813 | ; TB_LINE_COUNT: Returns the number of lines in the buffer.
814 | tb_line_count:
815 | ld bc, (_lcount)
816 | ret
817 |
818 |
819 | ; TB_PUTC: Stores a char in the text buffer.
820 | ; Args:
821 | ; - A: Char to be stored.
822 | ; Preserves:
823 | ; - DE, HL
824 | tb_putc:
825 | push de
826 | push hl
827 |
828 | ; First check we can store
829 | ld hl, (_cend)
830 | ld de, (_ccur)
831 | scf
832 | ccf
833 | sbc hl,de
834 |
835 | ; We can't store if there is no more space. For now, just return.
836 | jr Z, @done
837 |
838 |
839 | ; First we add the char to the text buffer.
840 | ld de, (_ccur)
841 | ld (de), a
842 | inc de
843 | ld (_ccur), de
844 |
845 | ; Now update the number of chars for the current line.
846 | ld hl, (_szptr)
847 | inc hl
848 | ld (_szptr), hl
849 |
850 | ; Update the character position.
851 | ld hl, (_cpos)
852 | inc hl
853 | ld (_cpos), hl
854 |
855 | ; Finally, update the character count.
856 | ld hl, (_ccount)
857 | inc hl
858 | ld (_ccount), hl
859 |
860 | @done:
861 | ; Now we are done.
862 | pop hl
863 | pop de
864 | ret
865 |
866 |
867 | ; TB_GETTAIL: Returns all characters at the end of the line.
868 | ; Returns:
869 | ; - HL: Pointer to start of tail
870 | ; - BC: Number of chars in tail til EOL.
871 | ; Preserves:
872 | ; - DE
873 | tb_gettail:
874 | push de
875 |
876 | ; We want to count how many chars we have between _cend and _elptr.
877 | ld bc, 0
878 | ld hl, (_elptr)
879 | ld de, (_cend)
880 |
881 | ; Get the number of characters.
882 | scf
883 | ccf
884 | sbc hl, de
885 |
886 | ; In order to get the full 24 bit value, we need to store the data
887 | ; in memory.
888 | push hl
889 | ld hl, 0
890 | add hl, sp
891 | ld bc, (hl)
892 | pop hl
893 |
894 | ; We now return _cend and count of chars in bc.
895 | ld hl, (_cend)
896 | pop de
897 | ret
898 |
899 |
900 | ; TB_GETHEAD: Returns all characters from start of line to current cursor.
901 | ; Returns:
902 | ; - HL: Pointer to start of head.
903 | ; - BC: Number of chars in tail til cursor.
904 | ; Preserves:
905 | ; - DE
906 | tb_gethead:
907 | push de
908 |
909 | ; Count how many chars we have between start of line and current
910 | ; cursor position
911 | ld bc, 0
912 | ld hl, (_ccur)
913 | ld de, (_slptr)
914 |
915 | scf
916 | ccf
917 | sbc hl, de
918 |
919 | ; Because its a 24 bit value, we need to store it in memory to copy it
920 | ; to BC.
921 | push hl
922 | ld hl, 0
923 | add hl, sp
924 | ld bc, (hl)
925 | pop hl
926 |
927 | ; We now return _slptr and count of chars in bc.
928 | ld hl, (_slptr)
929 | pop de
930 | ret
931 |
932 |
933 | ; TB_LEFT: Move the cursor left.
934 | ; Returns:
935 | ; - A: The character that the cursor was pointing to before moving.
936 | ; - B: 0 if move was successful.
937 |
938 | tb_left:
939 | ; Preserve registers
940 | push hl
941 | push de
942 |
943 | ; Get cursor and check if we can move left.
944 | ld hl, (_ccur)
945 | ld de, (_slptr)
946 | scf
947 | sbc hl, de
948 | jp P, @move
949 |
950 | ; Can't move.
951 | ld b, 1
952 | jr @done
953 |
954 | @move:
955 | ; Move _ccur and _cend pointers to the left.
956 | ld hl, (_ccur)
957 | ld de, (_cend)
958 | dec de
959 | dec hl
960 |
961 | ; Move char in _ccur to _cend
962 | ld a, (hl)
963 | ld (de), a
964 |
965 | ; Store updated pointers.
966 | ld (_cend), de
967 | ld (_ccur), hl
968 |
969 | ; Decrement charactor position
970 | ld hl, (_cpos)
971 | dec hl
972 | ld (_cpos), hl
973 |
974 | ; Signal we moved.
975 | ld b, 0
976 |
977 | @done:
978 | ; Restore registers.
979 | pop de
980 | pop hl
981 | ret
982 |
983 |
984 | ; TB_HOME: Move the cursor to the start of line.
985 | tb_home:
986 | push af
987 | push bc
988 |
989 | @loop:
990 | call tb_left
991 | ld a, b
992 | or a
993 | jr Z, @loop
994 |
995 | @done:
996 | pop af
997 | pop bc
998 | ret
999 |
1000 |
1001 | ; TB_DELETE: Remove a character to the right of the cursor.
1002 | tb_delete:
1003 | ; Preserve registers
1004 | push hl
1005 | push de
1006 |
1007 | ; Delete is equivalent to advancing the _cend pointer.
1008 | ld hl, (_elptr)
1009 | ld de, (_cend)
1010 | scf
1011 | ccf
1012 | sbc hl, de
1013 | jr Z, @done
1014 |
1015 | ; We have chars to delete. Do it.
1016 | inc de
1017 | ld (_cend), de
1018 |
1019 | @done:
1020 | pop de
1021 | pop hl
1022 | ret
1023 |
1024 | ; TB_BKSPACE: Move the cursor left, delete the character to the left.
1025 | tb_bkspace:
1026 | ; Preserve registers
1027 | push hl
1028 | push de
1029 |
1030 | ; Get cursor and check if we can move left.
1031 | ld hl, (_ccur)
1032 | ld de, (_slptr)
1033 | scf
1034 | sbc hl, de
1035 | jp P, @move
1036 |
1037 | ; Can't move.
1038 | ld b, 1
1039 | jr @done
1040 |
1041 | @move:
1042 | ; Move _ccur left, effectively erasing the character.
1043 | ld hl, (_ccur)
1044 | dec hl
1045 |
1046 | ; Store the updated pointer
1047 | ld (_ccur), hl
1048 |
1049 | ; Decrement character position
1050 | ld hl, (_cpos)
1051 | dec hl
1052 | ld (_cpos), hl
1053 |
1054 | ; Signal we moved
1055 | ld b, 0
1056 |
1057 | @done:
1058 | ; Restore registers
1059 | pop de
1060 | pop hl
1061 | ret
1062 |
1063 |
1064 | ; TB_RIGHT: Move the cursor right.
1065 | ; Returns:
1066 | ; - A: The character that the cursor is now pointing after moving.
1067 | ; - B: 0 if the move was successful.
1068 | tb_right:
1069 | ; Preserve registers
1070 | push hl
1071 | push de
1072 |
1073 | ; Get cursor and check if we can move right.
1074 | ld hl, (_elptr)
1075 | ld de, (_cend)
1076 | scf
1077 | sbc hl, de
1078 | jp P, @move
1079 |
1080 | ; Can't move.
1081 | ld b, 1
1082 | jr @done
1083 |
1084 | @move:
1085 | ; Copy char in _cend to _ccur
1086 | ld hl, (_cend)
1087 | ld de, (_ccur)
1088 | ld a, (hl)
1089 | ld (de), a
1090 |
1091 | ; Move pointers to the right
1092 | inc de
1093 | inc hl
1094 |
1095 | ; Store updated pointers
1096 | ld (_ccur), de
1097 | ld (_cend), hl
1098 |
1099 | ; Increment character position
1100 | ld hl, (_cpos)
1101 | inc hl
1102 | ld (_cpos), hl
1103 |
1104 | ; Signal we were able to move.
1105 | ld b, 0
1106 |
1107 | @done:
1108 | ; Restore registers.
1109 | pop de
1110 | pop hl
1111 | ret
1112 |
1113 |
1114 | ; TB_NEWLINE: Add a newline to the text buffer.
1115 | tb_newline:
1116 | ; First we add CRLF to the current line.
1117 | ld a, '\r'
1118 | call tb_putc
1119 | ld a, '\n'
1120 | call tb_putc
1121 |
1122 | ; TODO(icc): things to be done still.
1123 |
1124 |
1125 |
--------------------------------------------------------------------------------
/src/char_buffer.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include "char_buffer.h"
20 |
21 | #include
22 |
23 | char_buffer* cb_init(char_buffer* cb, int size) {
24 | cb->buf_ = (char*) malloc(sizeof(char) * size);
25 | if (cb->buf_ == NULL) {
26 | return NULL;
27 | }
28 |
29 | cb->size_ = size;
30 | cb_clear(cb);
31 | return cb;
32 | }
33 |
34 | void cb_destroy(char_buffer* cb) {
35 | free(cb->buf_);
36 | }
37 |
38 | void cb_clear(char_buffer* cb) {
39 | cb->curr_ = cb->buf_;
40 | cb->cend_ = cb->buf_ + cb->size_;
41 | }
42 |
43 | int cb_size(char_buffer* cb) {
44 | return cb->size_;
45 | }
46 |
47 | int cb_available(char_buffer* cb) {
48 | return cb->size_ - cb_used(cb);
49 | }
50 |
51 | int cb_used(char_buffer* cb) {
52 | char* end = cb->buf_ + cb->size_;
53 | int total = 0;
54 |
55 | total += (cb->curr_ - cb->buf_);
56 | total += (end - cb->cend_);
57 | return total;
58 | }
59 |
60 | void cb_put(char_buffer* cb, char ch) {
61 | *cb->curr_ = ch;
62 | cb->curr_++;
63 | }
64 |
65 | bool cb_del(char_buffer* cb) {
66 | const char* end = cb->buf_+cb->size_;
67 | const bool ok = cb->cend_ < end;
68 | if (ok) {
69 | cb->cend_++;
70 | }
71 | return ok;
72 | }
73 |
74 | bool cb_bksp(char_buffer* cb) {
75 | const bool bk = cb->curr_ > cb->buf_;
76 | if (bk) {
77 | cb->curr_--;
78 | }
79 | return bk;
80 | }
81 |
82 | char cb_prev(char_buffer* cb, int cnt) {
83 | const bool pr = cb->curr_ > cb->buf_;
84 | if (!pr) {
85 | return 0;
86 | }
87 |
88 | while (cnt-- > 0 && cb->curr_ > cb->buf_) {
89 | cb->cend_--;
90 | cb->curr_--;
91 | *cb->cend_ = *cb->curr_;
92 | }
93 | return *cb->cend_;
94 | }
95 |
96 | char cb_next(char_buffer* cb, int cnt) {
97 | const char* end = cb->buf_+cb->size_;
98 | if (cb->cend_ >= end) {
99 | return 0;
100 | }
101 |
102 | while (cnt-- > 0 && cb->cend_ < end) {
103 | *cb->curr_ = *cb->cend_;
104 | cb->curr_++;
105 | cb->cend_++;
106 | }
107 |
108 | if (cb->cend_ < end) {
109 | return *cb->cend_;
110 | }
111 | return 0;
112 | }
113 |
114 | char cb_peek(char_buffer* cb) {
115 | const char* end = cb->buf_ + cb->size_;
116 | if (cb->cend_ == end) {
117 | return 0;
118 | }
119 | return *cb->cend_;
120 | }
121 |
122 | char* cb_prefix(char_buffer* cb, int* sz) {
123 | *sz = (cb->curr_ - cb->buf_);
124 | if (*sz == 0) {
125 | return NULL;
126 | }
127 | return cb->buf_;
128 | }
129 |
130 | char* cb_suffix(char_buffer* cb, int* sz) {
131 | const char* end = cb->buf_ + cb->size_;
132 | *sz = end - cb->cend_;
133 | if (*sz == 0) {
134 | return NULL;
135 | }
136 | return cb->cend_;
137 | }
138 |
--------------------------------------------------------------------------------
/src/char_buffer.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifndef _CHAR_BUFFER_H_
20 | #define _CHAR_BUFFER_H_
21 |
22 | #include
23 | #include
24 |
25 | typedef struct _char_buffer {
26 | int size_;
27 | char* buf_;
28 | char* curr_;
29 | char* cend_;
30 | } char_buffer;
31 |
32 | // Setup ops.
33 | char_buffer* cb_init(char_buffer* cb, int size);
34 | void cb_destroy(char_buffer* cb);
35 | void cb_clear(char_buffer* cb);
36 |
37 | // Info ops.
38 | int cb_size(char_buffer* cb);
39 | int cb_available(char_buffer* cb);
40 | int cb_used(char_buffer* cb);
41 |
42 | // Character ops.
43 | void cb_put(char_buffer* cb, char ch);
44 | bool cb_del(char_buffer* cb);
45 | bool cb_bksp(char_buffer* cb);
46 |
47 | // Cursor ops.
48 | char cb_next(char_buffer* cb, int cnt);
49 | char cb_prev(char_buffer* cb, int cnt);
50 |
51 | // Char read.
52 | char cb_peek(char_buffer* cb);
53 | char* cb_prefix(char_buffer* cb, int* sz);
54 | char* cb_suffix(char_buffer* cb, int* sz);
55 |
56 |
57 | #endif // _CHAR_BUFFER_H_
58 |
--------------------------------------------------------------------------------
/src/cmd_ops.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include "cmd_ops.h"
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 | #include "editor.h"
28 | #include "text_buffer.h"
29 | #include "screen.h"
30 | #include "user_input.h"
31 |
32 | #define SCR(ed) screen* scr = &ed->scr_
33 | #define UI(ed) user_input* ui = &ed->ui_
34 | #define TB(ed) text_buffer* tb = &ed->buf_
35 |
36 | static void fill_screen(screen* scr, text_buffer* tb) {
37 | scr_clear_textarea(scr, scr->topY_, scr->bottomY_);
38 |
39 | char tpos = tb_ypos(tb);
40 | for (char ypos = scr->topY_; ypos < scr->bottomY_; ypos++) {
41 | int sz = 0;
42 | char* suffix = tb_suffix(tb, &sz);
43 | scr_write_line(scr, ypos, suffix, sz);
44 |
45 | tb_down(tb);
46 | const int npos = tb_ypos(tb);
47 | if (npos == tpos) {
48 | break;
49 | }
50 | tpos = npos;
51 | }
52 | }
53 |
54 |
55 | static void refresh_screen(screen* scr, text_buffer* tb) {
56 | char currY = scr->currY_;
57 | char currX = scr->currX_;
58 |
59 | text_buffer cp;
60 | tb_copy(&cp, tb);
61 | tb_home(&cp);
62 | while (tb_ypos(&cp) > 1 && scr->currY_ > scr->topY_) {
63 | tb_up(&cp);
64 | scr->currY_--;
65 | }
66 | fill_screen(scr, &cp);
67 |
68 | vdp_cursor_tab(currY, currX);
69 | scr->currY_ = currY;
70 | scr->currX_ = currX;
71 | }
72 |
73 | static bool update_fname(screen* scr, user_input* ui, text_buffer* tb, char* prefill) {
74 | char* fname;
75 | int sz;
76 | RESPONSE res = ui_text(ui, scr, "File name: ", prefill, &fname, &sz);
77 | if (res == CANCEL_OPT) {
78 | return false;
79 | } else if (res == YES_OPT) {
80 | strncpy(tb->fname_, fname, sz);
81 | tb->fname_[(int)sz] = 0;
82 | return true;
83 | }
84 | return false;
85 | }
86 |
87 | static bool save_file(text_buffer* tb) {
88 | if (!tb_valid_file(tb)) {
89 | return false;
90 | }
91 |
92 | char fh = mos_fopen(tb->fname_, FA_WRITE | FA_CREATE_ALWAYS);
93 | if (fh == 0) {
94 | return false;;
95 | }
96 |
97 | char* prefix = NULL;
98 | char* suffix = NULL;
99 | int psz = 0;
100 | int ssz = 0;
101 | tb_content(tb, &prefix, &psz, &suffix, &ssz);
102 |
103 | if (prefix != NULL && psz > 0) {
104 | mos_fwrite(fh, (char*) prefix, psz);
105 | }
106 | if (suffix != NULL && ssz > 0) {
107 | mos_fwrite(fh, (char*) suffix, ssz);
108 | }
109 |
110 | mos_fclose(fh);
111 | tb_saved(tb);
112 |
113 | return true;
114 | }
115 |
116 | bool cmd_save(editor* ed) {
117 | TB(ed);
118 | SCR(ed);
119 | UI(ed);
120 |
121 | if (!tb_changed(tb)) {
122 | return true;
123 | }
124 |
125 | if (!tb_valid_file(tb) && !update_fname(scr, ui, tb, NULL)) {
126 | return false;
127 | }
128 |
129 | return save_file(tb);
130 | }
131 |
132 |
133 | void cmd_save_as(editor* ed) {
134 | TB(ed);
135 | SCR(ed);
136 | UI(ed);
137 |
138 | if (update_fname(scr, ui, tb, tb->fname_)) {
139 | save_file(tb);
140 | }
141 | }
142 |
143 |
144 | bool cmd_quit(editor* ed) {
145 | TB(ed);
146 | SCR(ed);
147 | UI(ed);
148 |
149 | if (!tb_changed(tb)) {
150 | return true;
151 | }
152 |
153 | RESPONSE res = ui_dialog(ui, scr, "Save before quit?");
154 | if (res == NO_OPT) {
155 | return true;
156 | }
157 | if (res == CANCEL_OPT) {
158 | return false;
159 | }
160 |
161 | return cmd_save(ed);
162 | }
163 |
164 | void cmd_color_picker(editor* ed) {
165 | SCR(ed);
166 | UI(ed);
167 |
168 | RESPONSE ret = ui_color_picker(ui, scr);
169 | if (ret == YES_OPT) {
170 | TB(ed);
171 | char ch = tb_peek(tb);
172 | scr_clear(scr);
173 | refresh_screen(scr, tb);
174 | scr_show_cursor_ch(scr, ch);
175 | }
176 | }
177 |
178 | void cmd_putc(editor* ed, key k) {
179 | TB(ed);
180 | SCR(ed);
181 |
182 | if (k.key == '\t') {
183 | // Convert tab to spaces because it is too damn hard to get it working correctly with line scrolling.
184 | k.key = ' ';
185 | const char spaces = scr->tab_size_ - ((tb_xpos(tb)-1) % scr->tab_size_);
186 | for (char i = 0; i < spaces; i++) {
187 | cmd_putc(ed, k);
188 | }
189 | return;
190 | }
191 |
192 | tb_put(tb, k.key);
193 | split_line ln = tb_curr_line(tb);
194 | scr_putc(scr, k.key, ln.prefix_, ln.psz_, ln.suffix_, ln.ssz_);
195 | }
196 |
197 | static inline void reset_viewport() {
198 | putch(26);
199 | }
200 |
201 | static void define_viewport(char left, char bottom, char right, char top) {
202 | static char viewport[5] = {28, 0, 0, 0, 0};
203 | viewport[1] = left;
204 | viewport[2] = bottom;
205 | viewport[3] = right;
206 | viewport[4] = top;
207 | VDP_PUTS(viewport);
208 | }
209 |
210 | static void scroll_down(
211 | screen* scr, char topY, char bottomY, char* line, int sz, char ch) {
212 | static const char down[] = {23, 7, 0, 2, 8};
213 | define_viewport(0, bottomY, scr->cols_, topY);
214 | VDP_PUTS(down);
215 | reset_viewport();
216 | scr_write_line(scr, scr->currY_, line, sz);
217 | vdp_cursor_tab(scr->currY_, scr->currX_);
218 | scr_show_cursor_ch(scr, ch);
219 | }
220 |
221 | static void scroll_up(
222 | screen* scr, char topY, char bottomY, char* line, int sz, char ch) {
223 | static const char up[] = {23, 7, 0, 3, 8};
224 | define_viewport(0, bottomY, scr->cols_, topY);
225 | VDP_PUTS(up);
226 | reset_viewport();
227 | scr_write_line(scr, scr->currY_, line, sz);
228 | vdp_cursor_tab(scr->currY_, scr->currX_);
229 | scr_show_cursor_ch(scr, ch);
230 | }
231 |
232 | static void region_up(screen* scr, text_buffer* tb, char ch) {
233 | int sz = 0;
234 | char* line = tb_suffix(tb, &sz);
235 | scroll_up(scr, scr->currY_, scr->bottomY_-1, line, sz, ch);
236 |
237 | int diff = scr->bottomY_ - scr->currY_ - 1;
238 | int last = 0;
239 | int curr = 0;
240 | while (diff-- > 0) {
241 | curr = tb_down(tb);
242 | if (curr == last) {
243 | scr_write_line(scr, scr->bottomY_-1, NULL, 0);
244 | return;
245 | }
246 | }
247 | line = tb_suffix(tb, &sz);
248 | scr_overwrite_line(scr, scr->bottomY_-1, line, sz, 255);
249 | }
250 |
251 | void cmd_show(editor* ed) {
252 | TB(ed);
253 | SCR(ed);
254 |
255 | text_buffer cb;
256 | tb_copy(&cb, tb);
257 | fill_screen(scr, &cb);
258 | vdp_cursor_tab(scr->currY_, scr->currX_);
259 |
260 | const char to_ch = tb_peek(tb);
261 | scr_show_cursor_ch(scr, to_ch);
262 | }
263 |
264 | static void cmd_del_merge(editor* ed) {
265 | TB(ed);
266 | if (!tb_del_merge(tb)) {
267 | return;
268 | }
269 | SCR(ed);
270 |
271 | const char ch = tb_peek(tb);
272 | text_buffer cp;
273 | tb_copy(&cp, tb);
274 | tb_home(&cp);
275 | region_up(scr, &cp, ch);
276 | }
277 |
278 | void cmd_del(editor* ed) {
279 | TB(ed);
280 | SCR(ed);
281 |
282 | if (tb_eol(tb)) {
283 | if (tb_bol(tb)) {
284 | cmd_del_line(ed);
285 | } else {
286 | cmd_del_merge(ed);
287 | }
288 | return;
289 | }
290 | if (!tb_del(tb)) {
291 | return;
292 | }
293 | int sz = 0;
294 | char* suffix = tb_suffix(tb, &sz);
295 | scr_del(scr, suffix, sz);
296 | }
297 |
298 | static void cmd_bksp_merge(editor* ed) {
299 | TB(ed);
300 | SCR(ed);
301 |
302 | if (!tb_bksp_merge(tb)) {
303 | return;
304 | }
305 | if (scr->currY_ > scr->topY_) {
306 | scr->currY_--;
307 | }
308 | if (tb->x_ > scr->cols_-1) {
309 | scr->currX_ = scr->cols_-1;
310 | } else {
311 | scr->currX_ = tb->x_;
312 | }
313 |
314 | char ch = tb_peek(tb);
315 | text_buffer cp;
316 |
317 | tb_copy(&cp, tb);
318 | tb_home(&cp);
319 | region_up(scr, &cp, ch);
320 | }
321 |
322 | void cmd_bksp(editor* ed) {
323 | TB(ed);
324 | SCR(ed);
325 |
326 | if (tb_bol(tb)) {
327 | if (tb_ypos(tb) > 1) {
328 | cmd_bksp_merge(ed);
329 | }
330 | return;
331 | }
332 |
333 | int sz = 0;
334 | char* suffix = tb_suffix(tb, &sz);
335 |
336 | if (!tb_bksp(tb)) {
337 | return;
338 | }
339 | scr_bksp(scr, suffix, sz);
340 | }
341 |
342 | void cmd_newl(editor* ed) {
343 | TB(ed);
344 | SCR(ed);
345 |
346 | char ch = tb_peek(tb);
347 | split_line ln = tb_curr_line(tb);
348 |
349 | if (!tb_newline(tb)) {
350 | return;
351 | }
352 | scr_write_line(scr, scr->currY_, ln.prefix_, ln.psz_);
353 |
354 | scr->currX_ = 0;
355 | if (scr->currY_ < scr->bottomY_-1) {
356 | scr->currY_++;
357 | scroll_down(scr, scr->currY_, scr->bottomY_-1, ln.suffix_, ln.ssz_, ch);
358 | } else {
359 | scroll_up(scr, scr->topY_, scr->bottomY_-1, ln.suffix_, ln.ssz_, ch);
360 | }
361 | }
362 |
363 | void cmd_del_line(editor* ed) {
364 | TB(ed);
365 | SCR(ed);
366 |
367 | if (!tb_del_line(tb)) {
368 | return;
369 | }
370 | scr->currX_ = 0;
371 |
372 | const char ch = tb_peek(tb);
373 | text_buffer cp;
374 |
375 | tb_copy(&cp, tb);
376 | tb_home(&cp);
377 | region_up(scr, &cp, ch);
378 | }
379 |
380 | void cmd_left(editor* ed) {
381 | TB(ed);
382 | SCR(ed);
383 |
384 | if (tb_bol(tb)) {
385 | if (tb_ypos(tb) > 1) {
386 | cmd_up(ed);
387 | cmd_end(ed);
388 | }
389 | return;
390 | }
391 | char from_ch = tb_peek(tb);
392 | char to_ch = tb_prev(tb);
393 |
394 | int sz = 0;
395 | char* suffix = tb_suffix(tb, &sz);
396 | scr_left(scr, from_ch, to_ch, 1, suffix, sz);
397 | }
398 |
399 | void cmd_w_left(editor* ed) {
400 | TB(ed);
401 | SCR(ed);
402 |
403 | if (tb_bol(tb)) {
404 | if (tb_ypos(tb) > 1) {
405 | cmd_up(ed);
406 | cmd_end(ed);
407 | }
408 | return;
409 | }
410 |
411 | const int from_x = tb_xpos(tb);
412 | const char from_ch = tb_peek(tb);
413 | const char to_ch = tb_w_prev(tb, from_ch);
414 | const char deltaX = from_x - tb_xpos(tb);
415 |
416 | int sz = 0;
417 | char* suffix = tb_suffix(tb, &sz);
418 | scr_left(scr, from_ch, to_ch, deltaX, suffix, sz);
419 | }
420 |
421 | void cmd_right(editor* ed) {
422 | TB(ed);
423 | SCR(ed);
424 |
425 | if (tb_eol(tb)) {
426 | int ypos = tb_ypos(tb);
427 | cmd_down(ed);
428 | if (ypos != tb_ypos(tb)) {
429 | cmd_home(ed);
430 | }
431 | return;
432 | }
433 |
434 | char from_ch = tb_peek(tb);
435 | if (from_ch == 0 ) {
436 | return;
437 | }
438 |
439 | const char to_ch = tb_next(tb);
440 | int sz = 0;
441 | char* prefix = tb_prefix(tb, &sz);
442 | scr_right(scr, from_ch, to_ch, 1, prefix, sz);
443 | }
444 |
445 | void cmd_w_right(editor* ed) {
446 | TB(ed);
447 | SCR(ed);
448 |
449 | if (tb_eol(tb)) {
450 | int ypos = tb_ypos(tb);
451 | cmd_down(ed);
452 | if (ypos != tb_ypos(tb)) {
453 | cmd_home(ed);
454 | }
455 | return;
456 | }
457 |
458 | const int from_x = tb_xpos(tb);
459 | const char from_ch = tb_peek(tb);
460 | const char to_ch = tb_w_next(tb, from_ch);
461 | const char deltaX = tb_xpos(tb) - from_x;
462 |
463 | int sz = 0;
464 | char* prefix = tb_prefix(tb, &sz);
465 | scr_right(scr, from_ch, to_ch, deltaX, prefix, sz);
466 | }
467 |
468 | void cmd_up(editor* ed) {
469 | TB(ed);
470 | SCR(ed);
471 |
472 | int psz = 0;
473 | char* prefix = tb_prefix(tb, &psz);
474 |
475 | int ypos = tb_ypos(tb);
476 | char from_ch = tb_peek(tb);
477 | char to_ch = tb_up(tb);
478 | if (ypos == tb_ypos(tb)) {
479 | return;
480 | }
481 |
482 | if (scr->currY_ == scr->topY_) {
483 | scr_hide_cursor_ch(scr, from_ch);
484 | scr->currX_ = 0;
485 | vdp_cursor_tab(scr->currY_, scr->currX_);
486 |
487 | tb_home(tb);
488 | to_ch = tb_peek(tb);
489 |
490 | char* suffix = tb_suffix(tb, &psz);
491 | scroll_down(scr, scr->topY_, scr->bottomY_-1, suffix, psz, to_ch);
492 | return;
493 | }
494 |
495 | if (scr->currX_ >= scr->cols_-1) {
496 | scr_write_line(scr, scr->currY_, prefix, psz);
497 | if (tb_xpos(tb) >= scr->cols_) {
498 | psz = 0;
499 | prefix = tb_prefix(tb, &psz);
500 | int pad = (tb_xpos(tb)-1) - scr->currX_;
501 | scr_write_line(scr, scr->currY_-1, prefix+pad, psz-pad);
502 | }
503 | }
504 |
505 | int xpos = tb_xpos(tb)-1;
506 | if (xpos > scr->cols_-1) {
507 | xpos = scr->cols_-1;
508 | }
509 | scr_up(scr, from_ch, to_ch, xpos);
510 | }
511 |
512 | void cmd_down(editor* ed) {
513 | TB(ed);
514 | SCR(ed);
515 |
516 | int ypos = tb_ypos(tb);
517 | int psz = 0;
518 | char* prefix = tb_prefix(tb, &psz);
519 | char from_ch = tb_peek(tb);
520 | char to_ch = tb_down(tb);
521 | if (ypos == tb_ypos(tb)) {
522 | return;
523 | }
524 | if (scr->currY_ >= scr->bottomY_-1) {
525 | scr_hide_cursor_ch(scr, from_ch);
526 | scr->currX_ = tb_xpos(tb)-1;
527 | if (scr->currX_ > scr->cols_-1) {
528 | scr->currX_ = scr->cols_-1;
529 | }
530 |
531 | text_buffer cp;
532 | tb_copy(&cp, tb);
533 | tb_home(&cp);
534 |
535 | int sz = 0;
536 | char* line = tb_suffix(&cp, &sz);
537 | scroll_up(scr, scr->topY_, scr->bottomY_-1, line, sz, to_ch);
538 | return;
539 | }
540 |
541 | if (scr->currX_ >= scr->cols_-1) {
542 | scr_write_line(scr, scr->currY_, prefix, psz);
543 | if (tb_xpos(tb) >= scr->cols_) {
544 | psz = 0;
545 | prefix = tb_prefix(tb, &psz);
546 | int pad = (tb_xpos(tb)-1) - scr->currX_;
547 | scr_write_line(scr, scr->currY_+1, prefix+pad, psz-pad);
548 | }
549 | }
550 |
551 | int xpos = tb_xpos(tb)-1;
552 | if (xpos > scr->cols_-1) {
553 | xpos = scr->cols_-1;
554 | }
555 | scr_down(scr, from_ch, to_ch, xpos);
556 | }
557 |
558 | void cmd_home(editor* ed) {
559 | TB(ed);
560 | SCR(ed);
561 |
562 | if (tb_bol(tb)) {
563 | return;
564 | }
565 |
566 | char from_ch = tb_peek(tb);
567 | char to_ch = tb_home(tb);
568 | if (to_ch != 0) {
569 | int sz = 0;
570 | char* suffix = tb_suffix(tb, &sz);
571 | scr_home(scr, from_ch, tb_peek(tb), suffix, sz);
572 | }
573 | }
574 |
575 | void cmd_end(editor* ed) {
576 | TB(ed);
577 | SCR(ed);
578 |
579 | const int from_x = tb_xpos(tb);
580 | char from_ch = tb_peek(tb);
581 | char to_ch = tb_end(tb);
582 | const int deltaX = tb_xpos(tb) - from_x;
583 | if (deltaX > 0) {
584 | int sz = 0;
585 | char* prefix = tb_prefix(tb, &sz);
586 | scr_end(scr, from_ch, to_ch, deltaX, prefix, sz);
587 | }
588 | }
589 |
590 | void cmd_page_up(editor* ed) {
591 | TB(ed);
592 | SCR(ed);
593 |
594 | const int curr = scr->currY_ - scr->topY_;
595 | const int page = scr->bottomY_ - scr->topY_+1;
596 | int remaining = tb_ypos(tb)-1 - curr;
597 |
598 | if (remaining <= 0) {
599 | remaining = tb_ypos(tb)-1;
600 | scr->currY_ = scr->topY_;
601 | }
602 | for (int i = 0; i < page && remaining > 0; i++, remaining--) {
603 | tb_up(tb);
604 | }
605 |
606 | char ch = tb_peek(tb);
607 | scr->currX_ = tb_xpos(tb) < scr->cols_ ? tb_xpos(tb)-1 : scr->cols_-1;
608 | refresh_screen(scr, tb);
609 | scr_show_cursor_ch(scr, ch);
610 | }
611 |
612 | void cmd_page_down(editor* ed) {
613 | TB(ed);
614 | SCR(ed);
615 |
616 | const int curr = scr->bottomY_ - scr->currY_;
617 | const int page = scr->bottomY_ - scr->topY_;
618 | int remaining = tb_ymax(tb) - tb_ypos(tb) - curr + 1;
619 |
620 | if (remaining <= 0) {
621 | remaining = tb_ymax(tb) - tb_ypos(tb);
622 | scr->currY_ = scr->bottomY_-1;
623 | }
624 | for (int i = 0; i < page && remaining > 0; i++, remaining--) {
625 | tb_down(tb);
626 | }
627 | char ch = tb_peek(tb);
628 | scr->currX_ = tb_xpos(tb) < scr->cols_ ? tb_xpos(tb)-1 : scr->cols_-1;
629 | refresh_screen(scr, tb);
630 | scr_show_cursor_ch(scr, ch);
631 | }
632 |
633 | void cmd_goto(editor* ed) {
634 | SCR(ed);
635 | UI(ed);
636 | TB(ed);
637 |
638 | int line = 0;
639 | RESPONSE goto_line = ui_goto(ui, scr, &line);
640 | if (goto_line != YES_OPT) {
641 | return;
642 | }
643 |
644 | const int ypos = tb_ypos(tb);
645 | if (ypos == line) {
646 | return;
647 | }
648 |
649 | int diff = 0;
650 | scr_hide_cursor_ch(scr, tb_peek(tb));
651 | if (line < ypos) {
652 | for (; line < ypos; line++) {
653 | diff--;
654 | tb_up(tb);
655 | if (tb_ypos(tb) == 1) {
656 | break;
657 | }
658 | }
659 | } else {
660 | int curr = tb_ypos(tb);
661 | for (; ypos < line; line--) {
662 | tb_down(tb);
663 | const int nyp = tb_ypos(tb);
664 | if (nyp == curr) {
665 | break;
666 | }
667 | curr = nyp;
668 | diff++;
669 | }
670 | }
671 |
672 | diff = ((int)scr->currY_) + diff;
673 | if (diff < (int)scr->topY_) {
674 | scr->currY_ = scr->topY_;
675 | } else if (diff >= (int) scr->bottomY_) {
676 | scr->currY_ = scr->bottomY_-1;
677 | } else {
678 | scr->currY_ = diff;
679 | }
680 | vdp_cursor_tab(scr->currY_, scr->currX_);
681 |
682 | scr->currX_ = tb_xpos(tb) < scr->cols_ ? tb_xpos(tb)-1 : scr->cols_-1;
683 | refresh_screen(scr, tb);
684 | scr_show_cursor_ch(scr, tb_peek(tb));
685 | }
686 |
687 |
--------------------------------------------------------------------------------
/src/cmd_ops.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifndef _CMD_OPS_H_
20 | #define _CMD_OPS_H_
21 |
22 | #include
23 | #include
24 |
25 | #include "vkey.h"
26 |
27 | typedef struct _editor editor;
28 |
29 | typedef struct _key {
30 | char key;
31 | VKey vkey;
32 | } key;
33 |
34 | typedef void(*cmd_op)(editor* ed);
35 |
36 | void cmd_show(editor* ed);
37 | bool cmd_quit(editor* ed);
38 | bool cmd_save(editor* ed);
39 | void cmd_save_as(editor* ed);
40 | void cmd_color_picker(editor* ed);
41 |
42 | void cmd_putc(editor* ed, key k);
43 | void cmd_del(editor* ed);
44 | void cmd_bksp(editor* ed);
45 | void cmd_newl(editor* ed);
46 | void cmd_del_line(editor* ed);
47 | void cmd_left(editor* ed);
48 | void cmd_w_left(editor* ed);
49 | void cmd_w_right(editor* ed);
50 | void cmd_right(editor* ed);
51 | void cmd_up(editor* ed);
52 | void cmd_down(editor* ed);
53 | void cmd_home(editor* ed);
54 | void cmd_end(editor* ed);
55 | void cmd_page_up(editor* ed);
56 | void cmd_page_down(editor* ed);
57 | void cmd_goto(editor* ed);
58 |
59 | #endif // _CMD_OPS_H_
60 |
--------------------------------------------------------------------------------
/src/conv.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include "conv.h"
20 |
21 | #include
22 |
23 | static void reverse(char* buf, int sz) {
24 | int start = 0;
25 | int end = sz -1;
26 | while (start < end) {
27 | char ch = buf[start];
28 | buf[start] = buf[end];
29 | buf[end] = ch;
30 | ++start;
31 | --end;
32 | }
33 | }
34 |
35 | char* i2s(int num, char* buf, int sz) {
36 | const bool is_neg = num < 0;
37 | if (is_neg) {
38 | num = -num;
39 | }
40 |
41 | if (num == 0) {
42 | buf[0] = '0';
43 | buf[1] = '\0';
44 | return buf;
45 | }
46 |
47 | int i = 0;
48 | while (num != 0 && i < sz) {
49 | int rem = num % 10;
50 | buf[i++] = rem + '0';
51 | num = num / 10;
52 | }
53 |
54 | if (num != 0) {
55 | // Not enough buffer space.
56 | return NULL;
57 | }
58 | if (is_neg) {
59 | if (i > sz-2) {
60 | // Not enough space for negative sign.
61 | return NULL;
62 | }
63 | buf[i++] = '-';
64 | }
65 |
66 | reverse(buf, i);
67 | buf[i] = 0;
68 | return buf;
69 | }
70 |
71 |
--------------------------------------------------------------------------------
/src/conv.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifndef _CONV_H_
20 | #define _CONV_H_
21 |
22 | #include
23 |
24 | char* i2s(int num, char* buf, int sz);
25 |
26 | #endif // _CONV_H_
27 |
--------------------------------------------------------------------------------
/src/editor.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include "editor.h"
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 | #include "cmd_ops.h"
28 |
29 | #define DEFAULT_CURSOR 32
30 |
31 | editor* ed_init(editor* ed, int mem_kb, const char* fname) {
32 | screen* scr = scr_init(&ed->scr_, DEFAULT_CURSOR);
33 | if (!tb_init(&ed->buf_, scr->tab_size_, mem_kb, fname)) {
34 | return NULL;
35 | }
36 | if (!ui_init(&ed->ui_, 256, ed->scr_.bottomY_, ed->scr_.cols_)) {
37 | tb_destroy(&ed->buf_);
38 | return NULL;
39 | }
40 |
41 | if (tb_used(&ed->buf_) > 0) {
42 | cmd_show(ed);
43 | }
44 | return ed;
45 | }
46 |
47 | void ed_destroy(editor* ed) {
48 | ui_destroy(&ed->ui_);
49 | scr_destroy(&ed->scr_);
50 | tb_destroy(&ed->buf_);
51 | }
52 |
53 | #define CMD_PUTC (cmd_op) 0x01
54 | #define CMD_QUIT (cmd_op) 0x02
55 | #define CMD_SAVE (cmd_op) 0x03
56 | typedef struct _key_command {
57 | cmd_op cmd;
58 | key k;
59 | } key_command;
60 |
61 | key_command read_input();
62 |
63 | void ed_run(editor* ed) {
64 | text_buffer* buf = &ed->buf_;
65 | screen* scr = &ed->scr_;
66 |
67 | for (;;) {
68 | scr_footer(scr, tb_fname(buf), tb_changed(buf), tb_xpos(buf), tb_ypos(buf));
69 | key_command kc = read_input();
70 | if (kc.cmd == CMD_PUTC) {
71 | cmd_putc(ed, kc.k);
72 | } else if (kc.cmd == CMD_QUIT) {
73 | if (cmd_quit(ed)) {
74 | break;
75 | }
76 | } else if (kc.cmd == CMD_SAVE) {
77 | cmd_save(ed);
78 | } else if (kc.cmd != NULL) {
79 | kc.cmd(ed);
80 | }
81 | }
82 | vdp_clear_screen();
83 | }
84 |
85 | key_command ctrlCmds(key_command kc, char mods) {
86 | switch (kc.k.vkey) {
87 | case VK_q:
88 | case VK_Q:
89 | kc.cmd = CMD_QUIT;
90 | break;
91 | case VK_LEFT:
92 | case VK_KP_LEFT:
93 | kc.cmd = cmd_w_left;
94 | break;
95 | case VK_RIGHT:
96 | case VK_KP_RIGHT:
97 | kc.cmd = cmd_w_right;
98 | break;
99 | case VK_DELETE:
100 | case VK_KP_DELETE:
101 | case VK_d:
102 | case VK_D:
103 | kc.cmd = cmd_del_line;
104 | break;
105 | case VK_S:
106 | case VK_s:
107 | if (mods & MOD_ALT) {
108 | kc.cmd = cmd_save_as;
109 | } else {
110 | kc.cmd = CMD_SAVE;
111 | }
112 | break;
113 | case VK_C:
114 | case VK_c:
115 | if (mods & MOD_ALT) {
116 | kc.cmd = cmd_color_picker;
117 | }
118 | break;
119 | case VK_G:
120 | case VK_g:
121 | kc.cmd = cmd_goto;
122 | break;
123 | default:
124 | kc.cmd = NULL;
125 | break;
126 | }
127 | return kc;
128 | }
129 |
130 | key_command editCmds(key_command kc) {
131 | switch (kc.k.vkey) {
132 | case VK_LEFT:
133 | case VK_KP_LEFT:
134 | kc.cmd = cmd_left;
135 | break;
136 | case VK_RIGHT:
137 | case VK_KP_RIGHT:
138 | kc.cmd = cmd_right;
139 | break;
140 | case VK_BACKSPACE:
141 | kc.cmd = cmd_bksp;
142 | break;
143 | case VK_DELETE:
144 | case VK_KP_DELETE:
145 | kc.cmd = cmd_del;
146 | break;
147 | case VK_HOME:
148 | case VK_KP_HOME:
149 | kc.cmd = cmd_home;
150 | break;
151 | case VK_END:
152 | case VK_KP_END:
153 | kc.cmd = cmd_end;
154 | break;
155 | case VK_RETURN:
156 | case VK_KP_ENTER:
157 | kc.cmd = cmd_newl;
158 | break;
159 | case VK_UP:
160 | case VK_KP_UP:
161 | kc.cmd = cmd_up;
162 | break;
163 | case VK_DOWN:
164 | case VK_KP_DOWN:
165 | kc.cmd = cmd_down;
166 | break;
167 | case VK_PAGEUP:
168 | kc.cmd = cmd_page_up;
169 | break;
170 | case VK_PAGEDOWN:
171 | kc.cmd = cmd_page_down;
172 | break;
173 | default:
174 | break;
175 | }
176 | return kc;
177 | }
178 |
179 | key_command read_input() {
180 | key_command kc = {NULL, {'\0', VK_NONE}};
181 | kc.k.key = getch();
182 | kc.k.vkey = getsysvar_vkeycode();
183 |
184 | const char mods = getsysvar_keymods();
185 | if (mods & MOD_CTRL) {
186 | return ctrlCmds(kc, mods);
187 | }
188 |
189 | if (kc.k.key == '\t' || (kc.k.key != 0x7F && kc.k.key >= 32)) {
190 | kc.cmd = CMD_PUTC;
191 | } else {
192 | return editCmds(kc);
193 | }
194 |
195 | return kc;
196 | }
197 |
198 |
--------------------------------------------------------------------------------
/src/editor.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifndef _EDITOR_H_
20 | #define _EDITOR_H_
21 |
22 | #include
23 |
24 | #include "screen.h"
25 | #include "text_buffer.h"
26 | #include "user_input.h"
27 |
28 | typedef struct _editor {
29 | screen scr_;
30 | text_buffer buf_;
31 | user_input ui_;
32 | } editor;
33 |
34 | editor* ed_init(editor* ed, int mem_kb, const char* fname);
35 | void ed_destroy(editor* ed);
36 |
37 | void ed_run(editor* ed);
38 |
39 | #endif // _EDITOR_H_
40 |
--------------------------------------------------------------------------------
/src/line_buffer.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include "line_buffer.h"
20 |
21 | #include
22 | #include
23 | #include
24 |
25 | // Setup ops.
26 | line_buffer* lb_init(line_buffer* lb, int size) {
27 | lb->buf_ = (int*) malloc(size * sizeof(int));
28 | if (lb->buf_ == NULL) {
29 | return NULL;
30 | }
31 |
32 | memset(lb->buf_, 0, size);
33 | lb->size_ = size;
34 | lb->curr_ = lb->buf_;
35 | lb->cend_ = lb->buf_ + size;
36 | return lb;
37 | }
38 |
39 | void lb_destroy(line_buffer* lb) {
40 | free(lb->buf_);
41 | }
42 |
43 | // Info ops
44 | int lb_curr(line_buffer* lb) {
45 | return lb->curr_ - lb->buf_;
46 | }
47 | int lb_avai(line_buffer* lb) {
48 | return lb->cend_ - lb->curr_;
49 | }
50 | int lb_max(line_buffer* lb) {
51 | return lb->size_;
52 | }
53 | bool lb_last(line_buffer* lb) {
54 | return lb->cend_ == (lb->buf_ + lb->size_);
55 | }
56 |
57 | // Line ops.
58 | bool lb_cinc(line_buffer* lb) {
59 | (*lb->curr_) += 1;
60 | return true;
61 | }
62 | bool lb_cdec(line_buffer* lb) {
63 | int cur = *lb->curr_;
64 | if (cur > 0) {
65 | (*lb->curr_) = cur - 1;
66 | return true;
67 | }
68 | return false;
69 | }
70 | int lb_csize(line_buffer* lb) {
71 | return *lb->curr_;
72 | }
73 |
74 | // Cursor ops.
75 | bool lb_up(line_buffer* lb) {
76 | bool ok = lb->curr_ > lb->buf_;
77 | if (ok) {
78 | lb->cend_--;
79 | *lb->cend_ = *lb->curr_;
80 | lb->curr_--;
81 | }
82 | return ok;
83 | }
84 | bool lb_down(line_buffer* lb) {
85 | bool ok = lb->cend_ < (lb->buf_+lb->size_);
86 | if (ok) {
87 | lb->curr_++;
88 | *lb->curr_ = *lb->cend_;
89 | lb->cend_++;
90 | }
91 | return ok;
92 | }
93 | bool lb_new(line_buffer* lb, int size) {
94 | bool ok = lb->curr_ < lb->cend_;
95 | if (ok) {
96 | const int csz = *lb->curr_;
97 | *lb->curr_ = size;
98 | lb->curr_++;
99 | *lb->curr_ = (csz - size);
100 | }
101 | return ok;
102 | }
103 |
104 | bool lb_del(line_buffer* lb) {
105 | if (!lb_last(lb)) {
106 | *lb->curr_ = *lb->cend_;
107 | lb->cend_++;
108 | return true;
109 | }
110 |
111 | if (*lb->curr_ > 0) {
112 | lb->curr_ = 0;
113 | return true;
114 | }
115 | return false;
116 | }
117 |
118 | bool lb_merge_next(line_buffer* lb) {
119 | if (lb_last(lb)) {
120 | return false;
121 | }
122 |
123 | (*lb->curr_) += *lb->cend_;
124 | lb->cend_++;
125 | return true;
126 | }
127 |
128 | int lb_merge_prev(line_buffer* lb) {
129 | if (lb->curr_ == lb->buf_) {
130 | return -1;
131 | }
132 |
133 | const int curr = *lb->curr_;
134 | lb->curr_--;
135 | (*lb->curr_) -= 2;
136 | const int next = *lb->curr_;
137 | (*lb->curr_) += curr;
138 |
139 | return next;
140 | }
141 |
--------------------------------------------------------------------------------
/src/line_buffer.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifndef _LINE_BUFFER_H_
20 | #define _LINE_BUFFER_H_
21 |
22 | #include
23 | #include
24 |
25 | typedef struct _line_buffer {
26 | int size_;
27 | int* buf_;
28 | int* curr_;
29 | int* cend_;
30 | } line_buffer;
31 |
32 | // Setup ops.
33 | line_buffer* lb_init(line_buffer* lb, int size);
34 | void lb_destroy(line_buffer* lb);
35 |
36 | // Info ops
37 | int lb_curr(line_buffer* lb);
38 | int lb_avai(line_buffer* lb);
39 | int lb_max(line_buffer* lb);
40 | bool lb_last(line_buffer* lb);
41 |
42 | // Line ops.
43 | bool lb_cinc(line_buffer* lb);
44 | bool lb_cdec(line_buffer* lb);
45 | int lb_csize(line_buffer* lb);
46 |
47 | // Cursor ops.
48 | bool lb_up(line_buffer* lb);
49 | bool lb_down(line_buffer* lb);
50 | bool lb_new(line_buffer* lb, int size);
51 | bool lb_del(line_buffer* lb);
52 | bool lb_merge_next(line_buffer* lb);
53 | int lb_merge_prev(line_buffer* lb);
54 |
55 | #endif // _LINE_BUFFER_H_
56 |
--------------------------------------------------------------------------------
/src/main.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include "editor.h"
20 | #include "editor.h"
21 | #include "screen.h"
22 |
23 | #include
24 |
25 | int main(int argc, char** argv) {
26 | editor ed;
27 |
28 | const char* fname = NULL;
29 | if (argc > 1) {
30 | fname = argv[1];
31 | }
32 | if (!ed_init(&ed, 256, fname)) {
33 | return 1;
34 | }
35 | ed_run(&ed);
36 |
37 | ed_destroy(&ed);
38 | return 0;
39 | }
40 |
--------------------------------------------------------------------------------
/src/screen.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include "screen.h"
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include "conv.h"
27 |
28 | #define MAX_COLS 255
29 |
30 | void set_colours(char fg, char bg) {
31 | vdp_set_text_colour(fg);
32 | vdp_set_text_colour(bg+128);
33 | }
34 |
35 | void scr_show_cursor_ch(screen* scr, char ch) {
36 | if (ch == 0 || ch == '\r' || ch == '\n') {
37 | ch = scr->cursor_;
38 | }
39 |
40 | // First reverse colors
41 | set_colours(scr->bg_, scr->fg_);
42 |
43 | // Print the cursor;
44 | putch(ch);
45 | vdp_cursor_left();
46 |
47 | // Reverse colors back.
48 | set_colours(scr->fg_, scr->bg_);
49 | }
50 |
51 | static void scr_show_cursor(screen* scr) {
52 | scr_show_cursor_ch(scr, scr->cursor_);
53 | }
54 |
55 | static void vdp_puts(char* str, char sz) {
56 | volatile uint8_t* sysvar = mos_sysvars();
57 | sysvar[sysvar_vdp_pflags] = 0;
58 | mos_puts(str, sz, 0);
59 |
60 | for (;;) {
61 | waitvblank();
62 | sysvar = mos_sysvars();
63 | if ((sysvar[sysvar_vdp_pflags] & 0x04) != 0) {
64 | break;
65 | }
66 | }
67 | }
68 |
69 | static char getColorForCh(char ch) {
70 | static char getcol[7] = {23, 0, 0x84, 4, 0, 4, 0};
71 |
72 | vdp_cursor_tab(0,0);
73 | putch(ch);
74 |
75 | volatile char idx = 0;
76 | for (int i = 0; i < 1; i++) {
77 | waitvblank();
78 | volatile char* sysvar = (volatile char*) mos_sysvars();
79 | idx = sysvar[sysvar_scrpixelIndex];
80 | }
81 |
82 | vdp_puts(getcol, sizeof(getcol));
83 | for (int i = 0; i < 1; i++) {
84 | waitvblank();
85 | volatile char* sysvar = (volatile char*) mos_sysvars();
86 | idx = sysvar[sysvar_scrpixelIndex];
87 | }
88 |
89 | return idx;
90 | }
91 |
92 | static void get_active_colours(screen* scr) {
93 | static char logic[4] = {23, 0, 0xC0, 0};
94 | VDP_PUTS(logic);
95 |
96 | scr->fg_ = getColorForCh('*');
97 | scr->bg_ = getColorForCh(' ');
98 | set_colours(scr->fg_, scr->bg_);
99 | }
100 |
101 | screen *scr_init(screen* scr, char cursor) {
102 | static char disable_cursor_wrap[4] = {23, 16, 1, 0};
103 | VDP_PUTS(disable_cursor_wrap);
104 |
105 | vdp_cursor_enable(false);
106 | scr->rows_ = getsysvar_scrRows();
107 | scr->cols_ = getsysvar_scrCols();
108 | scr->colors_ = getsysvar_scrColours();
109 | scr->cursor_ = cursor;
110 | scr->topY_ = 1;
111 | scr->bottomY_ = scr->rows_-1;
112 | get_active_colours(scr);
113 | scr_clear(scr);
114 | scr_show_cursor(scr);
115 | vdp_cursor_home();
116 | scr->tab_size_ = 4;
117 |
118 | return scr;
119 | }
120 |
121 | void scr_destroy(screen* scr) {
122 | static char enable_cursor_wrap[4] = {23, 16, 1, 1};
123 | VDP_PUTS(enable_cursor_wrap);
124 | vdp_cursor_enable(true);
125 | scr->currX_ = 0;
126 | scr->currY_ = 0;
127 | scr->rows_ = 0;
128 | scr->cols_ = 0;
129 | }
130 |
131 | void scr_footer(screen* scr, char* fname, bool dirty, int x, int y) {
132 | static char* no_file = "[NO FILE]";
133 | if (fname == NULL) {
134 | fname = no_file;
135 | }
136 | const int fnsz = strlen(fname);
137 | int psz = 13 + fnsz ;
138 |
139 | vdp_cursor_tab(scr->bottomY_, 0);
140 | set_colours(scr->bg_, scr->fg_);
141 |
142 | mos_puts(fname, fnsz, 0);
143 | if (dirty) {
144 | putch('*');
145 | } else {
146 | putch(' ');
147 | }
148 | putch(' ');
149 | for (int i = 0; i < scr->cols_-psz; i++) {
150 | putch(' ');
151 | }
152 |
153 | static char digits[16];
154 | i2s(y, digits, 16);
155 | int dsz = strlen(digits);
156 | char max = strlen(digits) < 4 ? 4 - strlen(digits) : 4;
157 | for (int i = 0; i < max; i++) {
158 | putch(' ');
159 | }
160 | mos_puts(digits, dsz, 0);
161 | putch(',');
162 |
163 | i2s(x, digits, 16);
164 | dsz = strlen(digits);
165 | max = strlen(digits) < 6 ? 6 - strlen(digits) : 6;
166 | mos_puts(digits, dsz, 0);
167 | for (int i = 0; i < max; i++) {
168 | putch(' ');
169 | }
170 |
171 | set_colours(scr->fg_, scr->bg_);
172 | vdp_cursor_tab(scr->currY_, scr->currX_);
173 | }
174 |
175 | char* title = "AED: Another Text Editor";
176 | void scr_clear(screen* scr) {
177 | vdp_clear_screen();
178 | vdp_cursor_home();
179 | vdp_cursor_tab(0,0);
180 | const int len = strlen(title);
181 | const int banner = (scr->cols_ - len)/2;
182 | for (int i = 0; i < banner; i++) {
183 | putch('-');
184 | }
185 | set_colours(scr->bg_, scr->fg_);
186 | mos_puts(title, strlen(title), 0);
187 | set_colours(scr->fg_, scr->bg_);
188 | for (int i = 0; i < banner; i++) {
189 | putch('-');
190 | }
191 | scr->currX_ = 0;
192 | scr->currY_ = scr->topY_;
193 | vdp_cursor_tab(scr->currY_, scr->currX_);
194 | }
195 |
196 | void scr_hide_cursor_ch(screen* scr, char ch) {
197 | if (ch == 0 || ch == '\r' || ch == '\n') {
198 | ch = scr->cursor_;
199 | }
200 |
201 | set_colours(scr->fg_, scr->bg_);
202 | putch(ch);
203 | vdp_cursor_left();
204 | }
205 |
206 | static void scr_hide_cursor(screen* scr) {
207 | scr_hide_cursor_ch(scr, scr->cursor_);
208 | }
209 |
210 | void scr_putc(screen* scr, char ch, char* prefix, int psz, char* suffix, int ssz) {
211 | scr_hide_cursor(scr);
212 | if (scr->currX_ < scr->cols_-1) {
213 | putch(ch);
214 | scr->currX_++;
215 | if (suffix != NULL && ssz > 0) {
216 | int max = scr->cols_ - scr->currX_;
217 | for (int i = 0; i < ssz && i < max; i++) {
218 | putch(suffix[i]);
219 | }
220 | vdp_cursor_tab(scr->currY_, scr->currX_);
221 | scr_show_cursor_ch(scr, suffix[0]);
222 | } else {
223 | scr_show_cursor(scr);
224 | }
225 | } else {
226 | int pad = psz - scr->cols_+1;
227 | scr_write_line(scr, scr->currY_, prefix+pad, psz-pad-1);
228 | vdp_cursor_tab(scr->currY_, scr->currX_-1);
229 | putch(ch);
230 | if (ssz > 0) {
231 | scr_show_cursor_ch(scr, suffix[0]);
232 | } else {
233 | scr_show_cursor(scr);
234 | }
235 | }
236 | }
237 |
238 | static void print_suffix(screen* scr, char* suffix, int sz) {
239 | int i = 0;
240 | const int limit = scr->cols_ - scr->currX_;
241 | for (; i < sz && i < limit; i++) {
242 | putch(suffix[i]);
243 | }
244 | if (i < limit) {
245 | putch(' ');
246 | }
247 | vdp_cursor_tab(scr->currY_, scr->currX_);
248 | }
249 |
250 | void scr_del(screen* scr, char* suffix, int sz) {
251 | char ch = scr->cursor_;
252 | if (sz > 0) {
253 | ch = suffix[0];
254 | print_suffix(scr, suffix, sz);
255 | }
256 | scr_show_cursor_ch(scr, ch);
257 | }
258 |
259 | void scr_bksp(screen* scr, char* suffix, int sz) {
260 | if (scr->currX_ == 0) {
261 | return;
262 | }
263 | scr->currX_--;
264 | scr_hide_cursor(scr);
265 | vdp_cursor_tab(scr->currY_, scr->currX_);
266 |
267 | char ch = scr->cursor_;
268 | if (sz > 0) {
269 | ch = suffix[0];
270 | print_suffix(scr, suffix, sz);
271 | }
272 | scr_show_cursor_ch(scr, ch);
273 | }
274 |
275 | void scr_left(screen* scr, char from_ch, char to_ch, char deltaX, char* suffix, int sz) {
276 | int x = scr->currX_ - deltaX;
277 | if (x >= 0) {
278 | scr->currX_ -= deltaX;
279 | } else if (sz > 0) {
280 | scr->currX_ = 0;
281 | int max = scr->cols_ - scr->currX_;
282 | vdp_cursor_tab(scr->currY_, 0);
283 | for (int i = 0; i < max; i++) {
284 | putch(suffix[i]);
285 | }
286 | vdp_cursor_tab(scr->currY_, scr->currX_);
287 |
288 | }
289 | scr_hide_cursor_ch(scr, from_ch);
290 | vdp_cursor_tab(scr->currY_, scr->currX_);
291 | scr_show_cursor_ch(scr, to_ch);
292 | }
293 |
294 | void scr_right(screen* scr, char from_ch, char to_ch, char deltaX, char* prefix, int sz) {
295 | int x = scr->currX_ + deltaX;
296 | if (x < scr->cols_) {
297 | scr->currX_ = x;
298 | } else if (sz > 0) {
299 | scr->currX_ = scr->cols_-1;
300 | int pad = sz - scr->cols_ + 1;
301 |
302 | vdp_cursor_tab(scr->currY_, 0);
303 | for (int i = 0; i < scr->cols_; i++) {
304 | putch(prefix[i+pad]);
305 | }
306 | vdp_cursor_tab(scr->currY_, scr->currX_);
307 |
308 | }
309 | scr_hide_cursor_ch(scr, from_ch);
310 | vdp_cursor_tab(scr->currY_, scr->currX_);
311 | scr_show_cursor_ch(scr, to_ch);
312 | }
313 |
314 | void scr_home(screen* scr, char from_ch, char to_ch, char* suffix, int sz) {
315 | scr_hide_cursor_ch(scr, from_ch);
316 | scr->currX_ = 0;
317 | if (sz > 0) {
318 | scr_write_line(scr, scr->currY_, suffix, sz);
319 | }
320 | vdp_cursor_tab(scr->currY_, scr->currX_);
321 | scr_show_cursor_ch(scr, to_ch);
322 | }
323 |
324 | void scr_end(screen* scr, char from_ch, char to_ch, int deltaX, char* prefix, int sz) {
325 | scr_hide_cursor_ch(scr, from_ch);
326 | int x = (scr->currX_) + deltaX;
327 | if (x >= scr->cols_) {
328 | scr->currX_ = scr->cols_-1;
329 | } else {
330 | scr->currX_ = x;
331 | }
332 | if (sz > 0) {
333 | int pad = 0;
334 | if (sz >= scr->cols_) {
335 | pad = sz - scr->cols_+1;
336 | }
337 | scr_write_line(scr, scr->currY_, prefix+pad, sz-pad);
338 | }
339 | vdp_cursor_tab(scr->currY_, scr->currX_);
340 | scr_show_cursor_ch(scr, to_ch);
341 | }
342 |
343 | void scr_up(screen* scr, char from_ch, char to_ch, char currX) {
344 | scr_hide_cursor_ch(scr, from_ch);
345 | scr->currY_--;
346 | scr->currX_ = currX;
347 | vdp_cursor_tab(scr->currY_, scr->currX_);
348 | scr_show_cursor_ch(scr, to_ch);
349 | }
350 |
351 | void scr_down(screen* scr, char from_ch, char to_ch, char currX) {
352 | scr_hide_cursor_ch(scr, from_ch);
353 | scr->currY_++;
354 | scr->currX_ = currX;
355 | vdp_cursor_tab(scr->currY_, scr->currX_);
356 | scr_show_cursor_ch(scr, to_ch);
357 | }
358 |
359 | static void define_viewport(screen* scr, char top, char bottom) {
360 | static char viewport[5] = {28, 0, 0, 0, 0};
361 | viewport[2] = bottom;
362 | viewport[3] = scr->cols_;
363 | viewport[4] = top;
364 | VDP_PUTS(viewport);
365 | }
366 |
367 | void scr_clear_textarea(screen* scr, char top, char bottom) {
368 | define_viewport(scr, top, bottom);
369 | vdp_clear_screen();
370 | putch(26); // Reset viewport.
371 | }
372 |
373 | void scr_write_line(screen* scr, char ypos, char* buf, int sz) {
374 | scr_overwrite_line(scr, ypos, buf, sz, scr->cols_);
375 | }
376 |
377 | void scr_overwrite_line(screen* scr, char ypos, char* buf, int sz, int psz) {
378 | vdp_cursor_tab(ypos, 0);
379 | int i = 0;
380 | for (; i < sz && i < scr->cols_; i++) {
381 | putch(buf[i]);
382 | }
383 | for (; i < psz && i < scr->cols_; i++) {
384 | putch(' ');
385 | }
386 | vdp_cursor_tab(scr->currY_, scr->currX_);
387 | }
388 |
389 | void scr_erase(screen* scr, int sz) {
390 | sz = sz + scr->currX_;
391 | if (sz > scr->cols_) {
392 | sz = scr->cols_;
393 | }
394 | for (int i = scr->currX_; i < sz; ++i) {
395 | putch(' ');
396 | }
397 | vdp_cursor_tab(scr->currY_, scr->currX_);
398 | }
399 |
400 |
--------------------------------------------------------------------------------
/src/screen.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifndef _SCREEN_H_
20 | #define _SCREEN_H_
21 |
22 | #include
23 | #include
24 |
25 | typedef struct _screen {
26 | char rows_;
27 | char cols_;
28 | char colors_;
29 |
30 | char currX_;
31 | char currY_;
32 |
33 | char topY_;
34 | char bottomY_;
35 |
36 | char tab_size_;
37 | char cursor_;
38 | char fg_;
39 | char bg_;
40 | } screen;
41 |
42 | // Setup.
43 | screen* scr_init(screen* scr, char cursor);
44 | void scr_destroy(screen* scr);
45 | void scr_clear(screen* scr);
46 | void scr_footer(screen* scr, char* fname, bool dirty, int x, int y);
47 |
48 | // Input.
49 | void scr_putc(screen* scr, char ch, char* prefix, int psz, char* suffix, int ssz);
50 | void scr_del(screen* scr, char* suffix, int sz);
51 | void scr_bksp(screen* scr, char* suffix, int sz);
52 |
53 | // Navigation.
54 | void scr_left(screen* scr, char from_ch, char to_ch, char deltaX, char* suffix, int sz);
55 | void scr_right(screen* scr, char from_ch, char to_c, char deltaX, char* prefix, int sz);
56 | void scr_up(screen* scr, char from_ch, char to_ch, char currX);
57 | void scr_down(screen* scr, char from_ch, char to_ch, char currX);
58 | void scr_home(screen* scr, char from_ch, char to_ch, char* prefix, int sz);
59 | void scr_end(screen* scr, char from_ch, char to_ch, int deltaX, char* suffix, int sz);
60 |
61 | // Screen management.
62 | void set_colours(char fg, char bg);
63 | void scr_clear_textarea(screen* scr, char top, char bottom);
64 | void scr_write_line(screen* scr, char ypos, char* buf, int sz);
65 | void scr_overwrite_line(screen* scr, char ypos, char* buf, int sz, int psz);
66 |
67 | void scr_show_cursor_ch(screen* scr, char ch);
68 | void scr_hide_cursor_ch(screen* scr, char ch);
69 | void scr_erase(screen* scr, int sz);
70 |
71 | #endif // _SCREEN_H_
72 |
--------------------------------------------------------------------------------
/src/text_buffer.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include "text_buffer.h"
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 | text_buffer* tb_init(text_buffer* tb, char tab_size, int mem_kb, const char* fname) {
28 | int line_count = mem_kb << 5;
29 | int char_count = (mem_kb << 10) - line_count;
30 | if (!cb_init(&tb->cb_, char_count)) {
31 | return NULL;
32 | }
33 | if (!lb_init(&tb->lb_, line_count)) {
34 | cb_destroy(&tb->cb_);
35 | return NULL;
36 | }
37 | tb->x_ = 0;
38 | tb->fname_[0] = 0;
39 | tb->dirty_ = false;
40 |
41 | if (fname != NULL && !tb_load(tb, tab_size, fname)) {
42 | lb_destroy(&tb->lb_);
43 | cb_destroy(&tb->cb_);
44 | return NULL;
45 | };
46 | return tb;
47 | }
48 |
49 | void tb_destroy(text_buffer* tb) {
50 | cb_destroy(&tb->cb_);
51 | lb_destroy(&tb->lb_);
52 | }
53 |
54 | // Info ops.
55 | int tb_size(text_buffer* tb) {
56 | return cb_size(&tb->cb_);
57 | }
58 | int tb_available(text_buffer* tb) {
59 | return cb_available(&tb->cb_);
60 | }
61 | int tb_used(text_buffer* tb) {
62 | return cb_used(&tb->cb_);
63 | }
64 |
65 | #define IS_EOL(x) (x == 0 || (x >= 10 && x <= 13))
66 |
67 | bool tb_eol(text_buffer* tb) {
68 | const char ch = cb_peek(&tb->cb_);
69 | return IS_EOL(ch);
70 | }
71 | bool tb_bol(text_buffer* tb) {
72 | return tb->x_ == 0;
73 | }
74 |
75 | char* tb_fname(text_buffer* tb) {
76 | if (tb->fname_[0] == 0) {
77 | return NULL;
78 | }
79 | return tb->fname_;
80 | }
81 |
82 | bool tb_changed(text_buffer* tb) {
83 | return tb->dirty_;
84 | }
85 |
86 | void tb_saved(text_buffer* tb) {
87 | tb->dirty_ = false;
88 | }
89 |
90 | // Character ops.
91 | void tb_put(text_buffer* tb, char ch) {
92 | cb_put(&tb->cb_, ch);
93 | tb->x_++;
94 | lb_cinc(&tb->lb_);
95 | tb->dirty_ = true;
96 | }
97 |
98 | bool tb_del(text_buffer* tb) {
99 | if (cb_del(&tb->cb_)) {
100 | lb_cdec(&tb->lb_);
101 | tb->dirty_ = true;
102 | return true;
103 | }
104 | return false;
105 | }
106 |
107 | bool tb_bksp(text_buffer* tb) {
108 | const bool ok = cb_bksp(&tb->cb_);
109 | if (ok) {
110 | tb->x_--;
111 | lb_cdec(&tb->lb_);
112 | tb->dirty_ = true;
113 | }
114 | return ok;
115 | }
116 |
117 | bool tb_newline(text_buffer* tb) {
118 | tb->dirty_ = true;
119 | tb_put(tb, '\r');
120 | tb_put(tb, '\n');
121 | const bool ok = lb_new(&tb->lb_, tb->x_);
122 | if (ok) {
123 | tb->x_ = 0;
124 | }
125 | return ok;
126 | }
127 |
128 | bool tb_del_line(text_buffer* tb) {
129 | if (lb_last(&tb->lb_) && lb_csize(&tb->lb_) == 0) {
130 | return false;
131 | }
132 |
133 | tb_home(tb);
134 | while (lb_csize(&tb->lb_) > 0) {
135 | tb_del(tb);
136 | }
137 | lb_del(&tb->lb_);
138 | tb->dirty_ = true;
139 |
140 | return true;
141 | }
142 |
143 | bool tb_del_merge(text_buffer* tb) {
144 | if (lb_last(&tb->lb_) || !tb_eol(tb)) {
145 | return false;
146 | }
147 |
148 | // This function is only called when we are the end of the line.
149 | // If we are not the last, then we have a \r\n sequence.
150 | tb_del(tb);
151 | tb_del(tb);
152 | lb_merge_next(&tb->lb_);
153 | tb->dirty_ = true;
154 |
155 | return true;
156 | }
157 |
158 | bool tb_bksp_merge(text_buffer* tb) {
159 | if (!tb_bol(tb) || tb_ypos(tb) == 1) {
160 | return false;
161 | }
162 | cb_bksp(&tb->cb_);
163 | cb_bksp(&tb->cb_);
164 |
165 | tb->x_ = lb_merge_prev(&tb->lb_);
166 | tb->dirty_ = true;
167 | return true;
168 | }
169 |
170 |
171 | // Cursor ops.
172 | char tb_next(text_buffer* tb) {
173 | tb->x_++;
174 | return cb_next(&tb->cb_, 1);
175 | }
176 |
177 | static bool isstop(char ch) {
178 | switch (ch) {
179 | case '[':
180 | case ']':
181 | case '(':
182 | case ')':
183 | case '<':
184 | case '>':
185 | case ' ':
186 | case '\t':
187 | case ';':
188 | case ':':
189 | case '.':
190 | case ',':
191 | case '@':
192 | case '!':
193 | case '#':
194 | case '\\':
195 | case '/':
196 | return true;
197 | default:
198 | return false;
199 | }
200 | return false;
201 | }
202 |
203 | #define KEEP_SKIPPING(from_stopch, ch) \
204 | (!IS_EOL(ch) && ( \
205 | (from_stopch && isstop(ch)) || \
206 | (!from_stopch && !isstop(ch)) \
207 | ))
208 |
209 | char tb_w_next(text_buffer* tb, char from_ch) {
210 | const bool stopch = isstop(from_ch);
211 | char ch = 0;
212 | do {
213 | ch = cb_next(&tb->cb_, 1);
214 | tb->x_++;
215 | } while (KEEP_SKIPPING(stopch, ch));
216 |
217 | return ch;
218 | }
219 |
220 | char tb_prev(text_buffer* tb) {
221 | const char ch = cb_prev(&tb->cb_, 1);
222 | if (ch) {
223 | tb->x_--;
224 | }
225 | return ch;
226 | }
227 |
228 | char tb_w_prev(text_buffer* tb, char from_ch) {
229 | const bool stopch = isstop(from_ch);
230 | char ch = 0;
231 | do {
232 | ch = cb_prev(&tb->cb_, 1);
233 | tb->x_--;
234 | } while (tb->x_ > 0 && KEEP_SKIPPING(stopch, ch));
235 |
236 | return ch;
237 | }
238 |
239 | char tb_up(text_buffer* tb) {
240 | if (!lb_up(&tb->lb_)) {
241 | return 0;
242 | }
243 |
244 | const int sz = lb_csize(&tb->lb_);
245 | const int maxX = sz - 2;
246 | int back = sz + tb->x_;
247 | if (maxX < tb->x_) {
248 | tb->x_ = maxX;
249 | }
250 |
251 | return cb_prev(&tb->cb_, back - tb->x_);
252 | }
253 |
254 | char tb_down(text_buffer* tb) {
255 | int move = lb_csize(&tb->lb_) - tb->x_;
256 | if (!lb_down(&tb->lb_)) {
257 | return 0;
258 | }
259 | int cend = lb_csize(&tb->lb_);
260 | if (!lb_last(&tb->lb_)) {
261 | cend -= 2;
262 | }
263 | if (tb->x_ > cend) {
264 | tb->x_ = cend;
265 | }
266 | return cb_next(&tb->cb_, move + tb->x_);
267 | }
268 |
269 | char tb_home(text_buffer* tb) {
270 | const int back = tb->x_;
271 | tb->x_ = 0;
272 | return cb_prev(&tb->cb_, back);
273 | }
274 |
275 | char tb_end(text_buffer* tb) {
276 | char ch = cb_peek(&tb->cb_);
277 | while (!IS_EOL(ch)) {
278 | ch = cb_next(&tb->cb_, 1);
279 | tb->x_++;
280 | }
281 | return 0;
282 | }
283 |
284 |
285 | int tb_xpos(text_buffer* tb) {
286 | return tb->x_ + 1;
287 | }
288 |
289 | int tb_ypos(text_buffer* tb) {
290 | return lb_curr(&tb->lb_)+1;
291 | }
292 |
293 | int tb_ymax(text_buffer* tb) {
294 | return lb_max(&tb->lb_) - lb_avai(&tb->lb_) +1;
295 | }
296 |
297 | void tb_copy(text_buffer* dst, text_buffer* src) {
298 | dst->lb_.buf_ = src->lb_.buf_;
299 | dst->lb_.curr_ = src->lb_.curr_;
300 | dst->lb_.cend_ = src->lb_.cend_;
301 | dst->lb_.size_ = src->lb_.size_;
302 |
303 | dst->cb_.buf_ = src->cb_.buf_;
304 | dst->cb_.curr_ = src->cb_.curr_;
305 | dst->cb_.cend_ = src->cb_.cend_;
306 | dst->cb_.size_ = src->cb_.size_;
307 |
308 | dst->x_ = src->x_;
309 | dst->fname_[0] = 0;
310 | dst->dirty_ = false;
311 | }
312 |
313 | // Char read.
314 | char tb_peek(text_buffer* tb) {
315 | return cb_peek(&tb->cb_);
316 | }
317 |
318 | char* tb_prefix(text_buffer* tb, int* sz) {
319 | int psz = 0;
320 | char* prefix = cb_prefix(&tb->cb_, &psz);
321 | if (prefix == NULL) {
322 | return NULL;
323 | }
324 | prefix = prefix + (psz - tb->x_);
325 | *sz = tb->x_;
326 | return prefix;
327 | }
328 |
329 | char* tb_suffix(text_buffer* tb, int* sz) {
330 | char* suffix = cb_suffix(&tb->cb_, sz);
331 | if (suffix == NULL) {
332 | return NULL;
333 | }
334 |
335 | *sz = lb_csize(&tb->lb_) - tb->x_;
336 | if (!lb_last(&tb->lb_)) {
337 | *sz -= 2;
338 | }
339 | return suffix;
340 | }
341 |
342 | split_line tb_curr_line(text_buffer* tb) {
343 | split_line ln;
344 |
345 | ln.prefix_ = tb_prefix(tb, &ln.psz_);
346 | ln.suffix_ = tb_suffix(tb, &ln.ssz_);
347 | return ln;
348 | }
349 |
350 | void tb_content(text_buffer* tb, char** prefix, int* psz, char** suffix, int* ssz) {
351 | *prefix = cb_prefix(&tb->cb_, psz);
352 | *suffix = cb_suffix(&tb->cb_, ssz);
353 | }
354 |
355 | static void convert_tabs(char_buffer* cb, line_buffer* lb, int spaces){
356 | cb_del(cb);
357 | cb_put(cb, ' ');
358 | cb_prev(cb, 1);
359 | if (spaces > 0) {
360 | for (char i = 0; i < spaces; i++) {
361 | cb_put(cb, ' ');
362 | lb_cinc(lb);
363 | }
364 | }
365 | }
366 |
367 | static int ensure_newline(char_buffer* cb, line_buffer* lb) {
368 | int added = 0;
369 | const char pch = cb_prev(cb, 1);
370 | cb_next(cb, 1);
371 |
372 | if (pch != '\r') {
373 | cb_put(cb, '\r');
374 | lb_cinc(lb);
375 | added++;
376 | }
377 |
378 | lb_new(lb, lb_csize(lb));
379 | return added;
380 | }
381 |
382 | static bool tb_read(char fh, char tab_size, text_buffer* tb, int sz) {
383 | // In order to read the file to the text buffer, we move cend_ sz postions and then
384 | // pass it + sz as the buffer to read.
385 | char_buffer* cb = &tb->cb_;
386 | cb->cend_ -= sz;
387 | mos_fread(fh, (char*)cb->cend_, sz);
388 |
389 | // Now I need to update lb_ line buffer with the correct values.
390 | // There might be cases where the line endings are not \r\n so we correct for them.
391 | int xpos = 0;
392 | int added = 0;
393 | for (int i = 0; i < sz; i++) {
394 | lb_cinc(&tb->lb_);
395 | const char ch = cb_peek(cb);
396 | if (ch == '\n') {
397 | added += ensure_newline(&tb->cb_, &tb->lb_);
398 | xpos = 0;
399 | }
400 | if (ch == '\t') {
401 | const char spaces = tab_size - (xpos % tab_size);
402 | convert_tabs(&tb->cb_, &tb->lb_, spaces);
403 | xpos += spaces;
404 | added += spaces;
405 | }
406 | cb_next(cb, 1);
407 | xpos++;
408 | }
409 |
410 | const char ch = cb_peek(cb);
411 | if (ch == '\n') {
412 | added += ensure_newline(&tb->cb_, &tb->lb_);
413 | xpos = 0;
414 | } else if (ch == '\t') {
415 | const char spaces = tab_size - (xpos % tab_size);
416 | convert_tabs(&tb->cb_, &tb->lb_, spaces);
417 | xpos += spaces;
418 | added += spaces;
419 | }
420 |
421 | cb_prev(cb, sz+added);
422 | tb->dirty_ = added != 0;
423 |
424 | // Now move the line buffer back to the first line.
425 | while (lb_up(&tb->lb_)) ;
426 | return true;
427 | }
428 |
429 |
430 | bool tb_load(text_buffer* tb, char tab_size, const char* fname) {
431 | if (fname == NULL) {
432 | return false;
433 | }
434 |
435 | int fsz = strlen(fname);
436 | strncpy(tb->fname_, fname, fsz);
437 | tb->fname_[fsz] = 0;
438 |
439 | char fh = mos_fopen(tb->fname_, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
440 | if (fh == 0) {
441 | // Try to create the file.
442 | fh = mos_fopen(tb->fname_, FA_READ | FA_WRITE | FA_CREATE_ALWAYS);
443 | if (fh == 0) {
444 | char* msg = "invalid file";
445 | mos_puts(msg, strlen(msg), 0);
446 | tb->fname_[0] = 0;
447 | return false;
448 | }
449 | }
450 | FIL* fil = mos_getfil(fh);
451 | if (fil == NULL) {
452 | mos_fclose(fh);
453 | return false;
454 | }
455 |
456 | bool ok = true;
457 | int sz = (int) fil->obj.objsize;
458 | if (sz > 0) {
459 | ok = tb_read(fh, tab_size, tb, sz);
460 | }
461 | mos_fclose(fh);
462 |
463 | return ok;
464 | }
465 |
466 | bool tb_valid_file(text_buffer* tb) {
467 | return tb->fname_[0] != 0;
468 | }
469 |
--------------------------------------------------------------------------------
/src/text_buffer.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifndef _TEXT_BUFFER_H_
20 | #define _TEXT_BUFFER_H_
21 |
22 | #include "char_buffer.h"
23 | #include "line_buffer.h"
24 |
25 | typedef struct _text_buffer {
26 | char_buffer cb_;
27 | line_buffer lb_;
28 | int x_;
29 | bool dirty_;
30 |
31 | char fname_[256];
32 | } text_buffer;
33 |
34 | text_buffer* tb_init(text_buffer* tb, char tab_size, int mem_kb, const char* fname);
35 | void tb_destroy(text_buffer* tb);
36 |
37 | // Info ops.
38 | int tb_size(text_buffer* tb);
39 | int tb_available(text_buffer* tb);
40 | int tb_used(text_buffer* tb);
41 | bool tb_eol(text_buffer* tb);
42 | bool tb_bol(text_buffer* tb);
43 | char* tb_fname(text_buffer* tb);
44 | bool tb_changed(text_buffer* tb);
45 | void tb_saved(text_buffer* tb);
46 |
47 | // Character ops.
48 | void tb_put(text_buffer* tb, char ch);
49 | bool tb_del(text_buffer* tb);
50 | bool tb_bksp(text_buffer* tb);
51 | bool tb_newline(text_buffer* tb);
52 | bool tb_del_line(text_buffer* tb);
53 | bool tb_del_merge(text_buffer* tb);
54 | bool tb_bksp_merge(text_buffer* tb);
55 |
56 | // Cursor ops.
57 | char tb_next(text_buffer* tb);
58 | char tb_w_next(text_buffer* tb, char from_ch);
59 | char tb_prev(text_buffer* tb);
60 | char tb_w_prev(text_buffer* tb, char from_ch);
61 | char tb_home(text_buffer* tb);
62 | char tb_up(text_buffer* tb);
63 | char tb_down(text_buffer* tb);
64 | char tb_end(text_buffer* tb);
65 | int tb_xpos(text_buffer* tb);
66 | int tb_ypos(text_buffer* tb);
67 | int tb_ymax(text_buffer* tb);
68 |
69 | // Text read.
70 | char tb_peek(text_buffer* tb);
71 | char* tb_suffix(text_buffer* tb, int* sz);
72 | char* tb_prefix(text_buffer* tb, int* sz);
73 |
74 | typedef struct _split_line {
75 | int psz_;
76 | char* prefix_;
77 | int ssz_;
78 | char* suffix_;
79 | } split_line;
80 | split_line tb_curr_line(text_buffer* tb);
81 |
82 | bool tb_load(text_buffer* tb, char tab_size, const char* fname);
83 | void tb_content(text_buffer* tb, char** prefix, int* psz, char** suffix, int* ssz);
84 | bool tb_valid_file(text_buffer* tb);
85 | void tb_copy(text_buffer* dst, text_buffer* src);
86 |
87 | #endif // _TEXT_BUFFER_H_
88 |
--------------------------------------------------------------------------------
/src/user_input.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include "user_input.h"
20 |
21 | #include
22 | #include
23 | #include
24 |
25 | #include "vkey.h"
26 |
27 | user_input* ui_init(user_input* ui, int size, char ypos, char cols) {
28 | if (!cb_init(&ui->cb_, size)) {
29 | return NULL;
30 | }
31 | ui->ypos_ = ypos;
32 | ui->cols_ = cols;
33 | return ui;
34 | }
35 |
36 | void ui_destroy(user_input* ui) {
37 | cb_destroy(&ui->cb_);
38 | }
39 |
40 |
41 | static char goto_line[11] = "Goto line: ";
42 | static int atoi(char* str, char sz) {
43 | int v = 0;
44 | int mul = 1;
45 | for (char i = 1; i <= sz; i++) {
46 | v += (str[sz-i] - '0') * mul;
47 | mul *= 10;
48 | }
49 | return v;
50 | }
51 |
52 | RESPONSE ui_goto(user_input* ui, screen* scr, int* line) {
53 | scr_write_line(scr, ui->ypos_, goto_line, sizeof(goto_line));
54 | vdp_cursor_tab(ui->ypos_, sizeof(goto_line));
55 | scr_show_cursor_ch(scr, scr->cursor_);
56 |
57 | char_buffer* cb = &ui->cb_;
58 | cb_clear(cb);
59 |
60 | do {
61 | char key = getch();
62 | VKey vkey = getsysvar_vkeycode();
63 |
64 | if (key >= '0' && key <= '9') {
65 | cb_put(cb, key);
66 | putch(key);
67 | scr_show_cursor_ch(scr, cb_peek(cb));
68 | continue;
69 | }
70 |
71 | switch (vkey) {
72 | case VK_ESCAPE:
73 | return CANCEL_OPT;
74 | case VK_RETURN: {
75 | int sz = 0;
76 | char* buf = cb_prefix(cb, &sz);
77 | if (sz <= 0) {
78 | return CANCEL_OPT;
79 | }
80 | *line = atoi(buf, (char) sz);
81 | if (*line < 0) {
82 | return CANCEL_OPT;
83 | }
84 | return YES_OPT;
85 | }
86 | case VK_BACKSPACE:
87 | scr_hide_cursor_ch(scr, cb_peek(cb));
88 | if (cb_bksp(cb)) {
89 | vdp_cursor_left();
90 | }
91 | scr_show_cursor_ch(scr, cb_peek(cb));
92 | break;
93 | default:
94 | break;
95 |
96 | }
97 | } while (true);
98 |
99 | return CANCEL_OPT;
100 | }
101 |
102 |
103 | static const char col_select[39] = "Use UP/DOWN LEFT/RIGHT to select FG/BG";
104 |
105 | RESPONSE ui_color_picker(user_input* ui, screen* scr) {
106 | char fg = scr->fg_;
107 | char bg = scr->bg_;
108 |
109 | const int pad = (scr->cols_ - sizeof(col_select)) / 2;
110 | do {
111 | vdp_cursor_tab(ui->ypos_, 0);
112 | set_colours(fg, bg);
113 | for (int i = 0; i < pad; i++) {
114 | putch(' ');
115 | }
116 | VDP_PUTS(col_select);
117 | for (int i = 0; i <= pad; i++) {
118 | putch(' ');
119 | }
120 |
121 | getch();
122 | VKey vkey = getsysvar_vkeycode();
123 |
124 | switch (vkey) {
125 | case VK_ESCAPE:
126 | return CANCEL_OPT;
127 | case VK_UP:
128 | case VK_KP_UP:
129 | fg = (fg + 1) % scr->colors_;
130 | break;
131 | case VK_DOWN:
132 | case VK_KP_DOWN:
133 | if (fg == 0) {
134 | fg = scr->colors_-1;
135 | } else {
136 | fg = (fg - 1) % scr->colors_;
137 | }
138 | break;
139 | case VK_LEFT:
140 | case VK_KP_LEFT:
141 | if (bg == 0) {
142 | bg = scr->colors_-1;
143 | } else {
144 | bg = (bg - 1) % scr->colors_;
145 | }
146 | break;
147 | case VK_RIGHT:
148 | case VK_KP_RIGHT:
149 | bg = (bg + 1) % scr->colors_;
150 | break;
151 | case VK_RETURN:
152 | case VK_KP_ENTER:
153 | scr->fg_ = fg;
154 | scr->bg_ = bg;
155 | return YES_OPT;
156 | default:
157 | break;
158 | }
159 |
160 | } while (true);
161 |
162 | return CANCEL_OPT;
163 | }
164 |
165 | static const char options[13] = " [Y/N/ESC]: ";
166 |
167 | RESPONSE ui_dialog(user_input* ui, screen* scr, char* msg) {
168 | const int msz = strlen(msg);
169 | scr_write_line(scr, ui->ypos_, msg, msz);
170 | vdp_cursor_tab(ui->ypos_, msz);
171 | VDP_PUTS(options);
172 | scr_show_cursor_ch(scr, scr->cursor_);
173 |
174 | do {
175 | char key = getch();
176 | VKey vkey = getsysvar_vkeycode();
177 | if (vkey == VK_ESCAPE) {
178 | break;
179 | }
180 | if (key == 'Y' || key == 'y') {
181 | return YES_OPT;
182 | }
183 | if (key == 'N' || key == 'n') {
184 | return NO_OPT;
185 | }
186 | } while (true);
187 |
188 | return CANCEL_OPT;
189 | }
190 |
191 | RESPONSE ui_text(
192 | user_input* ui,
193 | screen* scr,
194 | char* title,
195 | char* prefill,
196 | char** buf,
197 | int* sz
198 | ) {
199 | const int msz = strlen(title);
200 | scr_write_line(scr, ui->ypos_, title, msz);
201 | vdp_cursor_tab(ui->ypos_, msz);
202 |
203 | *buf = NULL;
204 | *sz = 0;
205 | char_buffer* cb = &ui->cb_;
206 | cb_clear(cb);
207 |
208 | if (prefill != NULL) {
209 | for (unsigned int i = 0; i < strlen(prefill); i++) {
210 | const char ch = prefill[i];
211 | cb_put(cb, ch);
212 | putch(ch);
213 | }
214 | }
215 | scr_show_cursor_ch(scr, scr->cursor_);
216 |
217 | do {
218 | char key = getch();
219 | VKey vkey = getsysvar_vkeycode();
220 |
221 | if (key != 0x7F && key > 0x20) {
222 | cb_put(cb, key);
223 | putch(key);
224 | scr_show_cursor_ch(scr, cb_peek(cb));
225 | continue;
226 | }
227 |
228 | switch (vkey) {
229 | case VK_ESCAPE:
230 | return CANCEL_OPT;
231 | case VK_RETURN:
232 | case VK_KP_ENTER:
233 | if (cb_used(cb) == 0) {
234 | return CANCEL_OPT;
235 | } else {
236 | *buf = cb_prefix(cb, sz);
237 | return YES_OPT;
238 | }
239 | break;
240 | case VK_BACKSPACE:
241 | scr_hide_cursor_ch(scr, cb_peek(cb));
242 | if (cb_bksp(cb)) {
243 | vdp_cursor_left();
244 | }
245 | scr_show_cursor_ch(scr, cb_peek(cb));
246 | break;
247 | default:
248 | break;
249 | }
250 | } while (true);
251 |
252 | return CANCEL_OPT;
253 | }
254 |
255 |
--------------------------------------------------------------------------------
/src/user_input.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifndef _USER_INPUT_H_
20 | #define _USER_INPUT_H_
21 |
22 | #include "char_buffer.h"
23 | #include "screen.h"
24 |
25 | typedef enum _response {
26 | CANCEL_OPT = 0,
27 | YES_OPT = 1,
28 | NO_OPT = 2,
29 | } RESPONSE;
30 |
31 |
32 | typedef struct _user_input {
33 | char_buffer cb_;
34 | char ypos_;
35 | char cols_;
36 | } user_input;
37 |
38 | user_input* ui_init(user_input* ui, int size, char ypos, char cols);
39 | void ui_destroy(user_input* ui);
40 |
41 | RESPONSE ui_goto(user_input* ui, screen* scr, int* line);
42 | RESPONSE ui_color_picker(user_input* ui, screen* scr);
43 | RESPONSE ui_dialog(user_input* ui, screen* scr, char* msg);
44 | RESPONSE ui_text(
45 | user_input* ui,
46 | screen* scr,
47 | char* title,
48 | char* prefill,
49 | char** buf,
50 | int* sz);
51 |
52 | #endif // _USER_INPUT_H_
53 |
--------------------------------------------------------------------------------
/src/vkey.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 Igor Cananea
3 | * Author: Igor Cananea
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | // FabGL / vdp-gl virtual key mappings.
20 |
21 | typedef enum VirtualKey {
22 | VK_NONE, /**< No character (marks the first virtual key) */
23 |
24 | VK_SPACE, /**< Space */
25 |
26 | VK_0, /**< Number 0 */
27 | VK_1, /**< Number 1 */
28 | VK_2, /**< Number 2 */
29 | VK_3, /**< Number 3 */
30 | VK_4, /**< Number 4 */
31 | VK_5, /**< Number 5 */
32 | VK_6, /**< Number 6 */
33 | VK_7, /**< Number 7 */
34 | VK_8, /**< Number 8 */
35 | VK_9, /**< Number 9 */
36 | VK_KP_0, /**< Keypad number 0 */
37 | VK_KP_1, /**< Keypad number 1 */
38 | VK_KP_2, /**< Keypad number 2 */
39 | VK_KP_3, /**< Keypad number 3 */
40 | VK_KP_4, /**< Keypad number 4 */
41 | VK_KP_5, /**< Keypad number 5 */
42 | VK_KP_6, /**< Keypad number 6 */
43 | VK_KP_7, /**< Keypad number 7 */
44 | VK_KP_8, /**< Keypad number 8 */
45 | VK_KP_9, /**< Keypad number 9 */
46 |
47 | VK_a, /**< Lower case letter 'a' */
48 | VK_b, /**< Lower case letter 'b' */
49 | VK_c, /**< Lower case letter 'c' */
50 | VK_d, /**< Lower case letter 'd' */
51 | VK_e, /**< Lower case letter 'e' */
52 | VK_f, /**< Lower case letter 'f' */
53 | VK_g, /**< Lower case letter 'g' */
54 | VK_h, /**< Lower case letter 'h' */
55 | VK_i, /**< Lower case letter 'i' */
56 | VK_j, /**< Lower case letter 'j' */
57 | VK_k, /**< Lower case letter 'k' */
58 | VK_l, /**< Lower case letter 'l' */
59 | VK_m, /**< Lower case letter 'm' */
60 | VK_n, /**< Lower case letter 'n' */
61 | VK_o, /**< Lower case letter 'o' */
62 | VK_p, /**< Lower case letter 'p' */
63 | VK_q, /**< Lower case letter 'q' */
64 | VK_r, /**< Lower case letter 'r' */
65 | VK_s, /**< Lower case letter 's' */
66 | VK_t, /**< Lower case letter 't' */
67 | VK_u, /**< Lower case letter 'u' */
68 | VK_v, /**< Lower case letter 'v' */
69 | VK_w, /**< Lower case letter 'w' */
70 | VK_x, /**< Lower case letter 'x' */
71 | VK_y, /**< Lower case letter 'y' */
72 | VK_z, /**< Lower case letter 'z' */
73 | VK_A, /**< Upper case letter 'A' */
74 | VK_B, /**< Upper case letter 'B' */
75 | VK_C, /**< Upper case letter 'C' */
76 | VK_D, /**< Upper case letter 'D' */
77 | VK_E, /**< Upper case letter 'E' */
78 | VK_F, /**< Upper case letter 'F' */
79 | VK_G, /**< Upper case letter 'G' */
80 | VK_H, /**< Upper case letter 'H' */
81 | VK_I, /**< Upper case letter 'I' */
82 | VK_J, /**< Upper case letter 'J' */
83 | VK_K, /**< Upper case letter 'K' */
84 | VK_L, /**< Upper case letter 'L' */
85 | VK_M, /**< Upper case letter 'M' */
86 | VK_N, /**< Upper case letter 'N' */
87 | VK_O, /**< Upper case letter 'O' */
88 | VK_P, /**< Upper case letter 'P' */
89 | VK_Q, /**< Upper case letter 'Q' */
90 | VK_R, /**< Upper case letter 'R' */
91 | VK_S, /**< Upper case letter 'S' */
92 | VK_T, /**< Upper case letter 'T' */
93 | VK_U, /**< Upper case letter 'U' */
94 | VK_V, /**< Upper case letter 'V' */
95 | VK_W, /**< Upper case letter 'W' */
96 | VK_X, /**< Upper case letter 'X' */
97 | VK_Y, /**< Upper case letter 'Y' */
98 | VK_Z, /**< Upper case letter 'Z' */
99 |
100 | VK_GRAVEACCENT, /**< Grave accent: ` */
101 | VK_ACUTEACCENT, /**< Acute accent: ´ */
102 | VK_QUOTE, /**< Quote: ' */
103 | VK_QUOTEDBL, /**< Double quote: " */
104 | VK_EQUALS, /**< Equals: = */
105 | VK_MINUS, /**< Minus: - */
106 | VK_KP_MINUS, /**< Keypad minus: - */
107 | VK_PLUS, /**< Plus: + */
108 | VK_KP_PLUS, /**< Keypad plus: + */
109 | VK_KP_MULTIPLY, /**< Keypad multiply: * */
110 | VK_ASTERISK, /**< Asterisk: * */
111 | VK_BACKSLASH, /**< Backslash: \ */
112 | VK_KP_DIVIDE, /**< Keypad divide: / */
113 | VK_SLASH, /**< Slash: / */
114 | VK_KP_PERIOD, /**< Keypad period: . */
115 | VK_PERIOD, /**< Period: . */
116 | VK_COLON, /**< Colon: : */
117 | VK_COMMA, /**< Comma: , */
118 | VK_SEMICOLON, /**< Semicolon: ; */
119 | VK_AMPERSAND, /**< Ampersand: & */
120 | VK_VERTICALBAR, /**< Vertical bar: | */
121 | VK_HASH, /**< Hash: # */
122 | VK_AT, /**< At: @ */
123 | VK_CARET, /**< Caret: ^ */
124 | VK_DOLLAR, /**< Dollar: $ */
125 | VK_POUND, /**< Pound: £ */
126 | VK_EURO, /**< Euro: € */
127 | VK_PERCENT, /**< Percent: % */
128 | VK_EXCLAIM, /**< Exclamation mark: ! */
129 | VK_QUESTION, /**< Question mark: ? */
130 | VK_LEFTBRACE, /**< Left brace: { */
131 | VK_RIGHTBRACE, /**< Right brace: } */
132 | VK_LEFTBRACKET, /**< Left bracket: [ */
133 | VK_RIGHTBRACKET, /**< Right bracket: ] */
134 | VK_LEFTPAREN, /**< Left parenthesis: ( */
135 | VK_RIGHTPAREN, /**< Right parenthesis: ) */
136 | VK_LESS, /**< Less: < */
137 | VK_GREATER, /**< Greater: > */
138 | VK_UNDERSCORE, /**< Underscore: _ */
139 | VK_DEGREE, /**< Degree: ° */
140 | VK_SECTION, /**< Section: § */
141 | VK_TILDE, /**< Tilde: ~ */
142 | VK_NEGATION, /**< Negation: ¬ */
143 |
144 | VK_LSHIFT, /**< Left SHIFT */
145 | VK_RSHIFT, /**< Right SHIFT */
146 | VK_LALT, /**< Left ALT */
147 | VK_RALT, /**< Right ALT */
148 | VK_LCTRL, /**< Left CTRL */
149 | VK_RCTRL, /**< Right CTRL */
150 | VK_LGUI, /**< Left GUI */
151 | VK_RGUI, /**< Right GUI */
152 |
153 | VK_ESCAPE, /**< ESC */
154 |
155 | VK_PRINTSCREEN, /**< PRINTSCREEN */
156 | VK_SYSREQ, /**< SYSREQ */
157 |
158 | VK_INSERT, /**< INS */
159 | VK_KP_INSERT, /**< Keypad INS */
160 | VK_DELETE, /**< DEL */
161 | VK_KP_DELETE, /**< Keypad DEL */
162 | VK_BACKSPACE, /**< Backspace */
163 | VK_HOME, /**< HOME */
164 | VK_KP_HOME, /**< Keypad HOME */
165 | VK_END, /**< END */
166 | VK_KP_END, /**< Keypad END */
167 | VK_PAUSE, /**< PAUSE */
168 | VK_BREAK, /**< CTRL + PAUSE */
169 | VK_SCROLLLOCK, /**< SCROLLLOCK */
170 | VK_NUMLOCK, /**< NUMLOCK */
171 | VK_CAPSLOCK, /**< CAPSLOCK */
172 | VK_TAB, /**< TAB */
173 | VK_RETURN, /**< RETURN */
174 | VK_KP_ENTER, /**< Keypad ENTER */
175 | VK_APPLICATION, /**< APPLICATION / MENU key */
176 | VK_PAGEUP, /**< PAGEUP */
177 | VK_KP_PAGEUP, /**< Keypad PAGEUP */
178 | VK_PAGEDOWN, /**< PAGEDOWN */
179 | VK_KP_PAGEDOWN, /**< Keypad PAGEDOWN */
180 | VK_UP, /**< Cursor UP */
181 | VK_KP_UP, /**< Keypad cursor UP */
182 | VK_DOWN, /**< Cursor DOWN */
183 | VK_KP_DOWN, /**< Keypad cursor DOWN */
184 | VK_LEFT, /**< Cursor LEFT */
185 | VK_KP_LEFT, /**< Keypad cursor LEFT */
186 | VK_RIGHT, /**< Cursor RIGHT */
187 | VK_KP_RIGHT, /**< Keypad cursor RIGHT */
188 | VK_KP_CENTER, /**< Keypad CENTER key */
189 |
190 | VK_F1, /**< F1 function key */
191 | VK_F2, /**< F2 function key */
192 | VK_F3, /**< F3 function key */
193 | VK_F4, /**< F4 function key */
194 | VK_F5, /**< F5 function key */
195 | VK_F6, /**< F6 function key */
196 | VK_F7, /**< F7 function key */
197 | VK_F8, /**< F8 function key */
198 | VK_F9, /**< F9 function key */
199 | VK_F10, /**< F10 function key */
200 | VK_F11, /**< F11 function key */
201 | VK_F12, /**< F12 function key */
202 |
203 | VK_GRAVE_a, /**< Grave a: à */
204 | VK_GRAVE_e, /**< Grave e: è */
205 | VK_GRAVE_i, /**< Grave i: ì */
206 | VK_GRAVE_o, /**< Grave o: ò */
207 | VK_GRAVE_u, /**< Grave u: ù */
208 | VK_GRAVE_y, /**< Grave y: ỳ */
209 |
210 | VK_ACUTE_a, /**< Acute a: á */
211 | VK_ACUTE_e, /**< Acute e: é */
212 | VK_ACUTE_i, /**< Acute i: í */
213 | VK_ACUTE_o, /**< Acute o: ó */
214 | VK_ACUTE_u, /**< Acute u: ú */
215 | VK_ACUTE_y, /**< Acute y: ý */
216 |
217 | VK_GRAVE_A, /**< Grave A: À */
218 | VK_GRAVE_E, /**< Grave E: È */
219 | VK_GRAVE_I, /**< Grave I: Ì */
220 | VK_GRAVE_O, /**< Grave O: Ò */
221 | VK_GRAVE_U, /**< Grave U: Ù */
222 | VK_GRAVE_Y, /**< Grave Y: Ỳ */
223 |
224 | VK_ACUTE_A, /**< Acute A: Á */
225 | VK_ACUTE_E, /**< Acute E: É */
226 | VK_ACUTE_I, /**< Acute I: Í */
227 | VK_ACUTE_O, /**< Acute O: Ó */
228 | VK_ACUTE_U, /**< Acute U: Ú */
229 | VK_ACUTE_Y, /**< Acute Y: Ý */
230 |
231 | VK_UMLAUT_a, /**< Diaeresis a: ä */
232 | VK_UMLAUT_e, /**< Diaeresis e: ë */
233 | VK_UMLAUT_i, /**< Diaeresis i: ï */
234 | VK_UMLAUT_o, /**< Diaeresis o: ö */
235 | VK_UMLAUT_u, /**< Diaeresis u: ü */
236 | VK_UMLAUT_y, /**< Diaeresis y: ÿ */
237 |
238 | VK_UMLAUT_A, /**< Diaeresis A: Ä */
239 | VK_UMLAUT_E, /**< Diaeresis E: Ë */
240 | VK_UMLAUT_I, /**< Diaeresis I: Ï */
241 | VK_UMLAUT_O, /**< Diaeresis O: Ö */
242 | VK_UMLAUT_U, /**< Diaeresis U: Ü */
243 | VK_UMLAUT_Y, /**< Diaeresis Y: Ÿ */
244 |
245 | VK_CARET_a, /**< Caret a: â */
246 | VK_CARET_e, /**< Caret e: ê */
247 | VK_CARET_i, /**< Caret i: î */
248 | VK_CARET_o, /**< Caret o: ô */
249 | VK_CARET_u, /**< Caret u: û */
250 | VK_CARET_y, /**< Caret y: ŷ */
251 |
252 | VK_CARET_A, /**< Caret A: Â */
253 | VK_CARET_E, /**< Caret E: Ê */
254 | VK_CARET_I, /**< Caret I: Î */
255 | VK_CARET_O, /**< Caret O: Ô */
256 | VK_CARET_U, /**< Caret U: Û */
257 | VK_CARET_Y, /**< Caret Y: Ŷ */
258 |
259 | VK_CEDILLA_c, /**< Cedilla c: ç */
260 | VK_CEDILLA_C, /**< Cedilla C: Ç */
261 |
262 | VK_TILDE_a, /**< Lower case tilde a: ã */
263 | VK_TILDE_o, /**< Lower case tilde o: õ */
264 | VK_TILDE_n, /**< Lower case tilde n: ñ */
265 |
266 | VK_TILDE_A, /**< Upper case tilde A: Ã */
267 | VK_TILDE_O, /**< Upper case tilde O: Õ */
268 | VK_TILDE_N, /**< Upper case tilde N: Ñ */
269 |
270 | VK_UPPER_a, /**< primera: a */
271 | VK_ESZETT, /**< Eszett: ß */
272 | VK_EXCLAIM_INV, /**< Inverted exclamation mark: ! */
273 | VK_QUESTION_INV, /**< Inverted question mark : ? */
274 | VK_INTERPUNCT, /**< Interpunct : · */
275 | VK_DIAERESIS, /**< Diaeresis : ¨ */
276 | VK_SQUARE, /**< Square : ² */
277 | VK_CURRENCY, /**< Currency : ¤ */
278 | VK_MU, /**< Mu : µ */
279 |
280 | VK_aelig, /** Lower case aelig : æ */
281 | VK_oslash, /** Lower case oslash : ø */
282 | VK_aring, /** Lower case aring : å */
283 |
284 | VK_AELIG, /** Upper case aelig : Æ */
285 | VK_OSLASH, /** Upper case oslash : Ø */
286 | VK_ARING, /** Upper case aring : Å */
287 |
288 | // Japanese layout support
289 | VK_YEN,
290 | VK_MUHENKAN,
291 | VK_HENKAN,
292 | VK_KATAKANA_HIRAGANA_ROMAJI,
293 | VK_HANKAKU_ZENKAKU_KANJI,
294 | VK_SHIFT_0,
295 |
296 | VK_ASCII, /**< Specifies an ASCII code - used when virtual key is embedded in VirtualKeyItem structure and VirtualKeyItem.ASCII is valid */
297 | VK_LAST, // marks the last virtual key
298 |
299 | } VKey;
300 |
301 | #define MOD_CTRL 0x01
302 | #define MOD_LALT 0x02
303 | #define MOD_RALT 0x04
304 | #define MOD_ALT 0x06 // Either alt key is fine.
305 | #define MOD_SHFT 0x08
306 | #define MOD_GUI 0x10
307 | #define MOD_CAPS 0x20
308 | #define MOD_NUML 0x40
309 | #define MOD_SCRL 0x80
310 |
--------------------------------------------------------------------------------