Jump to content
IGNORED

New GUI for the Atari 8-bit


flashjazzcat

Recommended Posts

 

What can I say? Each demo is more and more impressive! Seriously Jon, I know you keep reminding us that there's a long way to go, but what you have there running on an 8 bit machine is simply amazing. Thank you for all the work you're putting in on this, it really is remarkable.

Link to comment
Share on other sites

 

What can I say? Each demo is more and more impressive! Seriously Jon, I know you keep reminding us that there's a long way to go, but what you have there running on an 8 bit machine is simply amazing. Thank you for all the work you're putting in on this, it really is remarkable.

 

Totally agree, amazing stuff Jon, to think its all in assembler too.

 

Just imagine walking into Silica shop in the day and seeing this on the monitors, people would have begged for it there and then. Obviously its needed the tech to have evolved to get to this but I just look at the old 8bit with that display and I say wow in my head, same as I did when I saw your word processor, games like Space Harrier and a load of other bits produced lately.

 

The serious geek in me just loves the GUI the most...

 

Thanks Jon, just seeing the demo is amazing....

Link to comment
Share on other sites

Thanks everyone: I don't want to seem like I'm plaguing the forum with teasers, but big strides have been made, and I'm sure you can all appreciate how motivational the good feedback is when I'm neck-deep in thousands of lines of a assembler and wondering quite how I ended up in the middle of it all! icon_mrgreen.gif

 

Will this run on a standard Atari 800 w/ 48K?

A delivery media has been proposed which will allow just that. I'll go into it more at the appropriate time, with the consent of the developer. I reckon there will be two or three different builds of the GUI for different hardware set-ups.

Edited by flashjazzcat
Link to comment
Share on other sites

Unbelievable!! Come on, tell the truth. Your doing graphic demos on your PC/Mac, right? :)

 

I can't believe how fast you got everything moving. The last demo had my jaw on the floor. I better not drink anything while I watch your progress. I'll wind up spraying my computer with coffee.

 

Allan

Link to comment
Share on other sites

Unbelievable!! Come on, tell the truth. Your doing graphic demos on your PC/Mac, right? icon_smile.gif

 

I can't believe how fast you got everything moving. The last demo had my jaw on the floor. I better not drink anything while I watch your progress. I'll wind up spraying my computer with coffee.

LOL. Believe me, the first time I got a window on the screen, even I was surprised by how fast the non-client area rendered, especially since the mouse is being sampled at 600Hz. The block clear, desktop fill, horizontal and vertical line drawing routines, and scroll bar rendering are extremely fast. The design on the window title bar is rendered as a series of horizontal line operations, which are then overwritten by the two controls and the title string.

 

Jeez man. This thing is cleaner and more responsive than GEM on a stock 1040. That is impressive.

It feels very much like an ST in operation.

 

HOWEVER... these are just empty windows, and a desktop with no icons. Once we have desktop icons, they'll have to be re-rendered every time the desktop's redrawn, and this happens every time a window moves. Then we have all the windows' client areas to redraw. That said, I see no practical reason why a window (assuming it's full of icons or filenames) should take longer than the big "Edit" menu to render (which is well under a second), so hopefully even a busy desktop with lots of icons and a couple of large windows shouldn't take more than a couple of seconds to render. Time will tell. icon_wink.gif

Edited by flashjazzcat
Link to comment
Share on other sites

HOWEVER... these are just empty windows, and a desktop with no icons. Once we have desktop icons, they'll have to be re-rendered every time the desktop's redrawn, and this happens every time a window moves. Then we have all the windows' client areas to redraw.

 

I'm completely out of my league here, so take my questions as aiming to understand the issue better, rather than (God forbid) correct you or offer solutions. That said, after the initial icon rendering, isn't the unchanged desktop just a bitmap over which you draw the active window? Or do I overestimate the available resources by assuming that the display bitmap is flipped onto the display from a place in memory at which it is rendered by compositing the unchanged and changed parts of the desktop? The earliest programming I did for graphics was the DOS mode/x days. I never really understood (but am wholly awed by) the whole "scanline by scanline" thing.

Link to comment
Share on other sites

I'm completely out of my league here, so take my questions as aiming to understand the issue better, rather than (God forbid) correct you or offer solutions. That said, after the initial icon rendering, isn't the unchanged desktop just a bitmap over which you draw the active window? Or do I overestimate the available resources by assuming that the display bitmap is flipped onto the display from a place in memory at which it is rendered by compositing the unchanged and changed parts of the desktop?

Hey - I don't mind explaining this stuff, since it's only a couple of months since I understood the concepts myself! I should really start writing up this project properly, but I'm too busy coding... icon_smile.gif

 

Anway, we don't really have enough memory (or we certainly musn't assume we have) to keep caching the background (the way we do with the drop down menus) when drawing windows. This means that every time a window moves or is resized, we have to redraw the whole desktop. Well - not all of it, since we could economize by using a "dirty rectangle" algorithm and only redrawing those areas of a window or the desktop which have changed/been revealed/etc. However, I think by the time we've processed all the intersecting rectangles, we might as well have redrawn everything. It probably depends on what's inside the windows (a word processor will have to use a lot of clever caching and smart redrawing).

 

Of course, the method you describe of successive caching all the backgrounds in layers would be the fastest of all. When the top-level window was moved, we could just grab a composite image of the desktop with all the underlying windows from a buffer some place and redraw nothing but the top level window. The trouble is, when you get into moving icons on the desktop around open windows, much of the cached bitmap becomes invalid anyway.

 

Actually, I will be re-using the buffer which caches the data under the menus quite extensively for other stuff when the menus are closed. For example, the 8KB buffer can serve as a large bitmask to make easy work of highlighting and drawing icons partially obscured by irregularly shaped groups of intersecting windows. We just make a "silhouette" mask of all the open windows, then draw the icons using that mask. No need, then, to redraw a window if you park an icon slightly underneath it.

 

Of course, the window manager can go berzerk with caching and bitmaps if it detects sufficient memory. There are all kinds of clever optimisations which I just haven't needed to worry about yet. The net result would then be that the more RAM you have, the faster the GUI will run.

Edited by flashjazzcat
Link to comment
Share on other sites

Do you use GTIA to draw icons? Sorry i'm learning the Atari assembler as I go.

We're running the whole thing in Antic mode 15 (graphics 8 ), so drawing icons - indeed, drawing anything - is a case of setting the relevant bits in the display RAM. Naturally the OS drawing routines are far too slow, so we have a bunch of custom routines to get lines and stuff on the screen. An icon is arranged in two parts: first, a list of bytes (2 x 16) representing the icon bitmap, and secondly, another 32 bytes representing the icon mask, which basically describes the "silhouette" of the icon against the background. To render the icon, we take a byte of the screen RAM, AND it with the complement of the corresponding byte of the icon mask, then OR that byte with the corresponding byte of the icon bitmap, before writing the byte back to the screen RAM. If the icon position isn't on byte boundaries, there's some bit shifting (rather: table lookups) involved, but that's the general idea. PMGs or other hardware features are not employed at the moment.

Edited by flashjazzcat
Link to comment
Share on other sites

It's very flexible. If the window client area has no objects set up inside of it (i.e. children), any mouse actions in the client area will be returned as: EVENT and X,Y. For Reversi, it's up to you whether you want to fill the client area with 64 clickable button objects, or draw the grid yourself and then use math to figure out what was clicked. The latter method will be used by the text editor, since it's impractical to define each letter as a clickable object and register it with the event handler.

 

As for device context, we can't afford too much indirection with the graphics system, but we will have a virtual coordinate system and clipping, so you'll be able to draw things to a virtual "canvas" and then blit the relevant (visible) portion to the screen. However, it will be much quicker to clip all the graphics operations to the visible area of the canvas and then map the display RAM back to the workspace when scrolling about.

 

There'll also be a special "toolbar" control (as well as progress bars, sliders, spinners, etc). I'm glad to hear you're keen to do some coding! icon_mrgreen.gif

 

Oh: as for custom controls, I think I touched on this a while back. You create your control (either from scratch, or based on an inherited existing control), then register its methods (rendering, clicking, selection, etc) with the event handler. I'm also working on a system for custom callbacks (so you can - for example - have a bunch of objects all of the same class, but all with different callback code for drags, double-clicks, get focus, etc; this will be most useful for dialogue controls and icons) which doesn't cause the objects to balloon in size (I'll most likely use a pointer to a custom callback jump table).

 

EDIT: Thanks for that link - it's most interesting reading. The section on windows makes me think a better idea would be for each object to have a single callback procedure, which could then have its own "case/if" clause for the message it's received. That avoids object bloat and offers maximum flexibility.

Edited by flashjazzcat
Link to comment
Share on other sites

Hi Jon,

 

interesting to see all those videos. They are really nice and the speed is amazing. Pitty you didn't show the WUDSN IDE Outline and Errors messages - I'm still too hesitant to create an own demo video for the IDE, though I'd really like to have one ;-)

 

Why do you use "L" from DOS in an already started emulator instead of "Compile & Run" from the IDE? Also you are not using .PROC/.ENDPROC, so how can you still handle all those labels? (<= just kidding).

Link to comment
Share on other sites

I think I just use Binary Load with DOS because it's the way I'm used to working. In any case, although the GUI would run without DOS at the moment, it won't be long before it makes calls to the FMS, so I'd have the revert to my original method later on anyway. :)

 

I've been meaning to populate the source code with procedure wrappers for some time... just haven't gotten around it yet!

Link to comment
Share on other sites

Hmm, with ATASM I used to compile the executable into the ATR as "AUTORUN.SYS" and start the ATR via Compile & Run in the cases when I needed the FMS. Seems that MADS is missing the option to compile into the ATR.... hmmmm... hmmmm... I remeber I had added the option to use "Compile & Run" with a batch script which was used by Bernd and HIAS for their (more complex) stuff. I'll check that out.

Link to comment
Share on other sites

Can you post some structured pseudocode that describes the implementation of the current Event Handler?

 

As I've said, many times, and will likely say many more times... Everything is looking AMAZING! My main concern is that the current method of using a Full Screen Re-draw for the Entire Display will eventually be a very big limiting factor in how far the GUI can be advanced, much later down the road.

 

I think that it would be a good idea for us to brainstorm on how to streamline & optimize the way Events are Managed, in the Back-End of the program, now, before you get so deep into the program, that it would be next to impossible to try to do anything to it, if things get crazy, later.

 

A few things are jumping out at me, to cause my concern. This is in no way a critique of your work, as it is fantastic... I'm just looking at it with the Eyes/Blinders (ha) that studying Computer Science provided me with (/enforced upon me to utilize... ha) when I studied it academically.

 

Well, actually that is a bit restrictive, as far as a declaration of my methodologies... I was largely looking at EVERYTHING as a Hacker does, on intuition, in a DIY fashion, unconsciously employing a "Bottom-Up" design process, whenever I was doing practically ANYTHING, from adolescence onward, until college. (...cues Supertramp's "Logical Song", lol...). It was a huge mindf*ck, initially, to be forced to do EVERYTHING "Top-Down" when I was taught/re-taught how academicians perceive the way that professional computer scientists are to do things.

 

Thankfully, the Brainwashing never COMPLETELY took, ha, but it did allow me to be able to have a Worldview that allows me to selectively pick & choose how to look at a problem, simultaneously juggling Top-Down & Bottom Up Strategies and then choosing (what I consider) the right problem-solving path for the occasion, so I can't really shake my fist at them, screaming, "FU!", too much...lol.... but, let's just say that the process to get to that point was not entirely "Painless"... lol.

 

So, with that disclaimer in place... ha... I'm willing to share a little of this pain with you. ha.

 

As artists, we know that when we sculpt with clay, or paint, or draw, we usually first conceive of the "Finished Product" of our labors (a VERY quick Top-Down sort of vision), then we begin creating from the Bottom Up. Sometimes, in drawing, or preparations for a painting, we may employ Rulers, for perspective (Top-Down design strategy), but often we don't, if we already can do it in our heads, without the actual pencil guide-lines.

 

*(As a side note, I would like to turn you on to Edward Hau, a Master of Perspective, who Art-Historians have somehow neglected to give props to... just doing my part to fix this horrible travesty... )

 

Conversely, "Mechanical Drawing" is 180 degrees opposite of freehand drawing, it is largely a Top-Down type of activity.

 

Programming is, many times, much more like Additive-Sculpture... working with clay or a similar plastic medium, to "Create Something from Nothing". On the other-hand, Subtractive-Sculpture (chiseling the form from rock) is a largely Top-Down type of activity. Additive-Sculpture is MUCH more Forgiving... just add or subtract more clay, when something doesn't look right, whereas, with Subtractive Sculpture, if you chiseled too far, you just majorly (& permanently) changed your original vision, for better or for worse (usually for worse)... so besides for an excruciatingly detailed Top-Down Design Strategy, you also must have a deft & dexterous feel for the hammer & chisel (Tools of Implementation), and fine hand-eye/muscle co-ordination (Skill, earned through experience). To chisel a perfect form from Marble is, indeed, a miraculous feat, and one that deserves much respect... if for no other reason than of appreciating the dogged persistence of the artist, and his/her dedication to the craft, let alone the beauty of its end-result. Marble is an Unforgiving Medium, but when carved with dexterity, it has Permanence that lasts through the ages.

 

With this in mind, lets consider Software Lifecycle... I've probably presented enough prologue for you to intuitively grasp what I'm getting at, metaphorically... The Concept of Permanence... to paraphrase the great automotive designer, Ferdinand Porsche, "...to Create Something that is Timeless, this is the Ultimate Goal".

 

A lot of software isn't created to be Timeless... In fact, quite to the contrary, it is created to be re-created, over & over, version by version, to generate a source of economic income, for the software company.

 

What you are doing, in essence, is creating a Permanent & Timeless piece of software, for a machine that will never be produced again. Actually, I find it amusing that your GUI is more "Modern" than any existing "Modern GUI", because it is being developed, RIGHT NOW, ha, it is THE most modern GUI Environment. ...& quite frankly, we have probably studied more "GUI-History" than 90% of the developers who have brought GUIs into the world. ...Interesting way of looking at things, but nonetheless, Totally True.

 

So, after this somewhat long-winded, coffee-driven moment of reflection, ha, lets go back into the art-studio, take a moment to step back from the creation, and get a good look at it, from a few feet back. It looks great, and is coming along splendidly... we can easily pull up a stool, have a smoke, and a cup of coffee, and just gaze at it in awe & wonder, at what has been created from nothing.

 

This is normal during the process of creation. What we want to avoid is the Horror of the "OH, my gawd, Nooooooo!" buzzkilling revelation, that snuffs out that Good Feeling... The Horror that occurs when you realize that there is SOME type of Fatal-Flaw, or miscalculation that became compounded over time, that may jeopardize the Perfect Execution of the Vision.

 

This Ghoulish, Diabolical Gremlin often only manifests itself once you are so far into doing something that you are looking at throwing away a lot of your work, just to fix the problem.

 

In an effort to not give this Gremlin a chance to offer you a hammer... lollol... Let's try to look at this, for a moment, with a Top-Down Strategy. It seems to me that Streamlining the Event Handler, Employing Interrupts, and Utilizing Player-Missile Graphics should be considered deeply at this juncture.

 

Once this is REALLY nailed down, we should analyze the entire construct from both a Bottom-Up, and a Top-Down perspective, just to be sure that we are not, metaphorically speaking, constructing this Colossus on swampland that we were mistakenly convinced was actually firm earth for a foundation.

 

I view your work as a Historical & Permanent addition to the Atari Legacy, so, in no way am I trying to come off as critical, I'm just pulling up a stool, during a break, checking it out with you, and sharing what I know, in an effort to help make this the Finest GUI Possible. It's totally up to you how you do it, I'm just sharing some food-for-thought, so that with these vittles, we are well-fortified, & fully Prepared & Ready to keep the Gremlins at bay...lol.

 

Other than that, it Looks Amazing, & Best Wishes!

Link to comment
Share on other sites

Lol! My edited version.

 

Can you post some structured pseudocode that describes the implementation of the current Event Handler?

 

It seems to me that Streamlining the Event Handler, Employing Interrupts, and Utilizing Player-Missile Graphics should be considered deeply at this juncture.

 

You have a lot of time on your hands.

 

That Edward Hau link is not accessible from my pesky work network. :(. I'll try again at home.

 

 

It was a nice to read.

Edited by R6502A
Link to comment
Share on other sites

You can cheat reversi with text, but paint might be a little harder. =)

 

Looking at this GUI system reminds me of another time when I saw a program on Atari and was absolutely amazed that the machine could do it...Envision. (Stop me if you've heard this one...) It allowed you to create really huge "drawings" by replacing the font set with your own bitmaps and then laying them out - I guess it's called tiling when you do it in a game - and it had an extremely cool, super smooth scrolling viewer for the files. It came with (or somehow I obtained) an excellent map of the good old Enterprise NCC-1701.

 

They must loaded the replacement font bitmaps over the defaults, the whole map as a list of character codes in memory, and then rendered the viewport. The smooth scrolling I have no idea how they did.

 

Are you using any of those paradigms in your work?

 

I wonder how do you plan to implement custom controls and drawing area?

Each GUI system should have Reversi:

 

rev16_2.png

 

and a Paint program :)

 

w101paint.gif

 

I may write one :)

Reversi board is 8x8 and could be done with clickable buttons (icons?), but the Paint would need a kind of device context.

Link to comment
Share on other sites

Lol! My edited version.

 

Can you post some structured pseudocode that describes the implementation of the current Event Handler?

 

It seems to me that Streamlining the Event Handler, Employing Interrupts, and Utilizing Player-Missile Graphics should be considered deeply at this juncture.

 

You have a lot of time on your hands.

He must have even more spare time than me. icon_smile.gif

 

Can you post some structured pseudocode that describes the implementation of the current Event Handler?

 

As I've said, many times, and will likely say many more times... Everything is looking AMAZING! My main concern is that the current method of using a Full Screen Re-draw for the Entire Display will eventually be a very big limiting factor in how far the GUI can be advanced, much later down the road.

 

I think that it would be a good idea for us to brainstorm on how to streamline & optimize the way Events are Managed, in the Back-End of the program, now, before you get so deep into the program, that it would be next to impossible to try to do anything to it, if things get crazy, later...

 

... Let's try to look at this, for a moment, with a Top-Down Strategy. It seems to me that Streamlining the Event Handler, Employing Interrupts, and Utilizing Player-Missile Graphics should be considered deeply at this juncture.

 

Once this is REALLY nailed down, we should analyze the entire construct from both a Bottom-Up, and a Top-Down perspective, just to be sure that we are not, metaphorically speaking, constructing this Colossus on swampland that we were mistakenly convinced was actually firm earth for a foundation.

 

I view your work as a Historical & Permanent addition to the Atari Legacy, so, in no way am I trying to come off as critical, I'm just pulling up a stool, during a break, checking it out with you, and sharing what I know, in an effort to help make this the Finest GUI Possible. It's totally up to you how you do it, I'm just sharing some food-for-thought, so that with these vittles, we are well-fortified, & fully Prepared & Ready to keep the Gremlins at bay...lol.

 

Other than that, it Looks Amazing, & Best Wishes!

Thanks. That dazzling exegesis immediately had me reaching for my cigarettes. It reminded me - in a rather obtuse way - of Oscar Wilde's stunning preface to The Picture of Dorian Gray.

Of course, I don't put out these videos and demos in the hope that a lot of yes men will say "Wow!" and massage my software developer ego. All the excitement, nevertheless, can often turn into hype, which would make the finished product suck hard if it didn't live up to expectations.

 

So - yes - it looks great, runs smoothly, one can hardly credit it's an 8-bit machine, etc. Believe me, I'm not letting any of that go to my head. We have a long way to go yet.

 

Anyway: to tackle the meat and potatoes of your... erm... question? icon_smile.gif

 

- Yes: I can post some structured pseudocode that describes the implementation of the event handler. This will be a natural by-product of the extensive, thesis-like documentation I ultimately intend to write, but I can sure bring forward a synopsised version of that section, and I'd be interested in the views of yourself and others. One thing I do want to avoid - however - is a GUI produced by a committee. icon_wink.gif

 

I will say now that the event handler and messaging system is not based on any other source/psuedo code or theoretical documentation. It has grown completely out of the needs of the system, is a mish-mash of gleaned insight from studying various GUI implementations written in C and advice from the consultants on this forum, and as such is as unique as it can be. If it is functionally closely similar to any existing implementation, that will be down to coincidence. That I have not followed a "standard" approach may be a bad thing, or it may - and I rather suspect this to be the case - be advantageous, since the event handler has been coded from the bottom up in 6502.

 

The event handler and messaging system are small, and so far handle all common mouse actions: clicking, double-clicking and dragging. Hover events are currently confined to the menu system, although there's nothing stopping an application querying which object the mouse is over before any button action takes place. The calls exist to do this.

 

As for the "Full Screen Re-draw for the Entire Display", I don't quite know what you're getting at. Re-draws apply to an object and its children. If you re-draw a dialogue, all the controls get redrawn. The desktop, underlying windows, menu bar, etc, do not get touched. If you pull down a menu, the underlying bitmap is cached, and the menu is drawn. No other re-draw messages are issued. If you move a scroll bar on a window full of file icons, the client area gets re-drawn, and nothing else. I you move an icon on the desktop, a mask is used to protect any open windows, and nothing but the moved icon and the reinstated background gets redrawn. If you move the top-level window, the underlying data will have been cached (in the same place as the menu cache), and if the cache buffer is intact, redrawing of the underlying windows will be unnecessary. The cache buffer will be extensible depending on RAM availability, so more cache equals faster GUI.

 

- Player missile graphics are not going to be considered - deeply or superficially - at this juncture. The whole system is deliberately bitmap based.

 

- Use of interrupts: this is of interest to me. Naturally we need an interrupt mechanism in place, and they aren't being used yet, except for the mouse sampling and rendering (the mouse being rendered - using a highly optimized algorithm relying on pre-bit-shifted bitmaps - entirely in the stage 2 VBL). Interrupts as a core element of the event handler: interesting. Let's hear more about it and I'll see how it sits with the existing approach.

 

I don't honestly think there's much scope for streamlining of code which is already this streamlined, but we shall see. The event handler is surprisingly brief and the processing overheads extremely slight. It should be noted that the brevity and efficiency of the event handler - already fairly complete - is at least partly responsible for the efficiency of the system in its current state.

 

As I've said before: folks are investing a great deal of expectation in this project, and I can understand why everyone wants it to come out right and be as efficient as possible. I welcome insight, expertise, and advice. That's why I'm developing the system "out in the open" (to a certain extent, at least: it's not open source as such). I definitely get the impression that flaws in the design are suspected, based on interpretation of a few comments I made on the GUI's functionality a little way back up the thread. This is understandable, since I have no OOP programming background, am not a C++ coder, and knew nothing about the construction of a GUI until two months ago. I'm learning as I go.

 

The fact is that handling events is not the overhead in this system. We can rapidly locate the object an event is happening to, even if there are hundreds of objects visible on the screen. We can do a lot of recursive tree/list walking in a hundred thousand cycles. The bottleneck is rendering. This GUI will succeed or fail largely on how localized the screen updates can be. It may be that the system is as fast as it is because I have already magically coded the world's most efficient event handler. icon_mrgreen.gif

 

But: You had me at "Interrupts". icon_smile.gif I just hope the explanation doesn't include the word "threaded"...

 

You can cheat reversi with text, but paint might be a little harder. =)

 

Looking at this GUI system reminds me of another time when I saw a program on Atari and was absolutely amazed that the machine could do it...Envision. (Stop me if you've heard this one...) It allowed you to create really huge "drawings" by replacing the font set with your own bitmaps and then laying them out - I guess it's called tiling when you do it in a game - and it had an extremely cool, super smooth scrolling viewer for the files. It came with (or somehow I obtained) an excellent map of the good old Enterprise NCC-1701.

 

They must loaded the replacement font bitmaps over the defaults, the whole map as a list of character codes in memory, and then rendered the viewport. The smooth scrolling I have no idea how they did.

 

Are you using any of those paradigms in your work?

Paint - Reversi - it will all come to pass.

 

As for the tiled editor: I wrote one in Turbo Basic years ago. It was a clone of "Page Marshall". Since the GUI is entirely bitmap based (i.e. the screen elements are mainly drawn using vertical and horizontal lines), the "tiling" paradigm has less relevance here than - say - with the character based GUI (which could be made to look quite rich and pleasing using tiled elements), but there's nothing stopping anyone writing a GUI version of "Page Marshall" somewhere down the line.

Edited by flashjazzcat
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...