 YouTube, but in from, yes, sound. It's a little low, maybe that's a little better. Hopefully the sound is not too bad. Let's see, what else here? This stuff out of the way. All right, how's it going? Be out of a graph over in the YouTube. Happy Friday to you. And we are just about ready. Good afternoon, DJ Devon. How's it going? Nice to see you. Let's see here. Oh, okay, there we go. All right, I think we are ready. So hello, good day. Happy Friday. Welcome to the start of the weekend, unofficially at least. For those that might be new, my name is Tim. This is the deep dive program. On this program, we are working on circuit Python stuff. It is more typically on this live stream is these days is Scott working on the circuit Python core. Scott's out this week. So I am here. I did fill in for a while, several months while he was out on parental leave. So, you know, if you have watched the deep dive for a while, then you'll be somewhat familiar with me. But if you have been watching for the last couple of weeks, then you'll be more familiar with Scott. And I can say welcome and hello. It's nice to meet you if that fits you. He, I believe will be back next week. As far as I know, I don't, you know, I can't commit to that on his behalf, obviously. So don't take my word for it. There's some chance that that's not the case, but to the best of my knowledge as of right now, he's planning to be back for next week. So he'll be here at this time. This is deep dive program. He tends to work a little bit, you know, he tends to go a little bit deeper even than I do most of the time. He's a lead core developer. So he spends a lot of time down in the C code that is CircuitPython itself and lots of the surrounding sort of infrastructure on that level. We do on my streams get down there sometimes, but not quite as deep as he does and not quite as frequently as he does. And so more often where you'll find is I'm working at the Python level, the libraries, the CircuitPython libraries that are written in Python code that allow it to interact with various drivers and helper utilities and things like that. If you are brand new to all of this and you don't know what I'm talking about first, thanks for sticking with us while I said all of that stuff. But the main thing this stream is about is CircuitPython. The main website for it is right here, circuitpython.org. You can head there if you'd like to learn more. You can also join us on the Discord, ADAFRU.IT slash Discord. There's a CircuitPython dev channel as well as a help with CircuitPython channel where you can discuss all things either development, CircuitPython related or project, CircuitPython related if you need to get help with something you're trying to do. CircuitPython is a version of Python that runs on tiny computers called microcontrollers. There's a bunch of pictures of those types of devices over here. It's basically a version of Python that runs on these small computers. These things typically plug into your computer with a USB cable that will show up like a thumb drive with a little Python code file on there which you can then edit and save. As soon as you save it, the computer that is on these little boards recognizes that you changed the file and goes ahead and resets itself and executes the new version of your code. That's kind of the high level 50,000 foot view of what CircuitPython is about. On today's stream, we'll be working more so in the Blinka land. Blinka is the CircuitPython compatibility layer that is made to work for Raspberry Pi and other single board computers as well as MicroPython devices. CircuitPython is a fork of MicroPython. Blinka allows you to either run CircuitPython code on MicroPython devices rather than CircuitPython ones or it allows you to run CircuitPython code on RaspberryPis and other single board computers or in the case of what I will actually be working on this evening, we're actually gonna be running Blinka code directly on my Linux PC. So we'll be executing code just on my PC, interacting with the Blinka library that, you know, that in between layer and we'll also be getting some display IO, Blinka display IO into the mix. Before I jump all the way right into that stuff, let me also just say thank you to Adafruit. This is their website, Adafruit.com. Of course we are on the Adafruit channel here so I imagine everyone is probably familiar with them. But Adafruit of course is the company that is the primary financial backer for CircuitPython. They're paying the folks who work on the CircuitPython project. There's a team of folks who work on the project full-time including Scott, the person who normally streams during this live stream. There are a handful of other folks on the team like myself who work on the project part-time and are paid by Adafruit to do so. So a huge thank you of course to Adafruit as always and if you would like to help support the project, one of the ways you can do that is just head over to Adafruit.com and purchase hardware from them. They design and manufacture the microcontrollers, the actual devices that can run CircuitPython. They also design and manufacture many sorts of add-on boards that you can hook up to your microcontrollers to interact with other hardware be it screens, lights, you know, rainbow LEDs, buttons, beepers, buzzers, sensors, pretty much any kind of thing you can imagine. Let me catch up on the chat. Hey question on that, how does the Blinka, let me, I think I didn't go back quite far enough, let me catch up, how's it going to the Shippu? Thank goodness, let's say it's Friday, yep, agreed there, how's it going Dexter? Nice to see you my friend, DJ Devin, looking forward to learning about new Blinka display IO stuff, yep, we're gonna be jumping into Blinka display IO. Question on that, how does Blinka work for the RP2040 devices, does it emulate, is it a translation layer? I would not be the best person to answer unfortunately, I honestly do not know. Yeah, I mean I don't believe it is emulating, my understanding is it's more of a translation layer, but the true answer is I just don't know. I don't have any experience using Blinka in its micropython capabilities. I have a fair amount of experience using it with the Raspberry Pi and the single board computers, the Linux PCs, but I do not have any in the micropython specifically, and so I don't actually know how that works. We gotta show times, interpreted it is, circuit python itself and by proxy I believe Blinka when it's running in micropython and also micropython as well is an interpreted language. Ignore me, no worries, no worries. Okay, yeah sorry I don't have the exact answer for you, maybe if you ask in circuit python dev or a good person to ask might be Melissa if you can catch her on a Monday around the meeting or in the weeds or something like that. She's kind of lead on sort of the Blinka initiative, she probably would have a much better idea of how that works with micropython. So where I'm at today is Blinka display IO, so specifically the Blinka library that's responsible for implementing the core module that's called display IO. If you wanna use that again on your Raspberry Pi you would need to use Blinka display IO. What I have to go along with that is this library which I made called Blinka display IO Pi game display. It's a bit of a mouthful, but basically what it does is instead, it gives you a display IO.display class, but instead of that class expecting to communicate over a spy bus or an I2C bus to some hardware physical TFT display, instead of that what this display class is doing is outputting to a Pi game window. For those that aren't familiar, Pi game is a Python library for making games. Generally speaking it basically allows you to spawn a window and then put some graphics and sounds and things into that window. We are spawning that window and then displaying content from our display class into that window. So that's how I'm running it on the PC and where I'm at specifically right now is when I run my simple test it doesn't crash, but nothing really happens. Of course I say it doesn't crash and it did crash so maybe it does crash, let me try it again. I don't know, I'm gonna call that one a fluke because it hadn't crashed before and that's the only time I've seen it do that. So maybe it does crash sometimes I guess, but most of the time or at least sometimes it doesn't crash right now. But it also does not show a window with my stuff in it. So that's what we're trying to fix right now. There was a new version of Blinkett display IO relatively recently. We could search that up if we wanted, let's see, MicroPython and CircuitPython are both interpreted versus Arduino for example which is compiled, GitHub. They're not classified as RTOS but it's close to an RTOS in my opinion. DJ Devon says in the chat, all good stuff. I don't know too much of the kind of like specifics of how a lot of those different things work. I do know CircuitPython just like DJ Devon says is interpreted. It can be at least that you're saving plain text and that it's interpreting that text as Python. It does have MPY which is sort of a compiled type of thing but it is optional. You can actually just pass in plain text as well. So Blinkett display IO right here, it was updated about two weeks ago. There was a couple of different relatively large PRs merged in. I think it was even the first major release if I recall right. Yeah, two weeks ago was 1.0, the first major release. So there were some breaking changes there. There was lots of new functionality added. There were lots of updates in Blinkett display IO to bring the API in line with the core display IO which had been changed over the years. So Blinkett display IO is now brought up with that but it has meant that our thing here doesn't work. So one thing I noticed is that I have this code which is specific to Blinkett display IO. I should say actually this is specific to the Pygame display library right here. In normal display IO land, we don't really have this concept but in the Pygame display land because it's a window on a PC, that window has an X icon in the corner, right? Top right corner, it has an X to close that window. This code right here is responsible for checking if that X has been clicked and if it has then this method should return true which means that we would go inside this if statement, we would print this thing, we would break, we're breaking out of the while true which is our main loop which means we are ending our program which is exactly what's happening actually. We can see our print statement right here, check quit was true but why? Because that should only be the case if we clicked on the X and we didn't because we didn't even see the window spawn, we didn't even have time. So inside check quit we have try catch for event in pygameevent.get. So I assume that's gonna give us a list of events. If the event type is pygame quit, then key event set join the thread stuff. I don't know exactly what that's about but it's something to do with the way we're doing threads pygame.quit return true. So my guess is maybe we have an error. Let's go as E, let's go print E. Let's just do print E or trace back. Can we do the full trace back? Let's just do print E first or let me see actually EDA. Is there a trace back with trace back based exception? There's trace back module. I do kind of want to print the full trace back. I think Python catch exception plus print trace back. You can import the trace back module or something which is actually something you can't do in CircuitPython but since we're in CPython on the PC right now we can make use of this. So we do sys, I don't know about, oh I guess there's two ways to do it, interesting. Okay, let's do trace back one. That's the one I think I've seen before. Dot format exact, that should be easy enough. Port this. One thing I found is the order of the import seems to matter quite a bit with this new version of Blinka display IO. I think maybe now there is some sort of threading logic that's internal to Blinka display IO and I think maybe there didn't used to be. I'm not positive if that's true all the way. I could be wrong, I could be misunderstanding or misinterpreting but that's what I think is there's some sort of multi-threading going on in there now that didn't used to be and that is somehow clashing with the multi-threading that was in the Pi game display and the weird consequence of it, the weird side effect of it is that the order you import these modules matters and if you import them in the quote unquote wrong order it makes the code take forever. How does it get the exception? This is weird, it doesn't say accept exception as E and then pass it in or do anything with it. It just somehow is able to inherently get it from this which is super weird. My guess is though we're getting an exception here that's returning true and then that's why we're quitting. That's my guess as to what's going on here. You could probably argue this should not return true if we get an error but we should also look at the error and figure out what it is. Pi game error during, check quit, let's do that. A bit of extra print there and let's run that again. Okay, Pi game error, video system not initialized. Video system not initialized. I wonder if this is like a race condition thing. If we did just return false from here, like could it work on the next iteration or something? Nah, we're just not ever spawning the video for some reason. Just always gives us this not initialized thing, okay. Video system not initialized. So I think there is a, there isn't a knit, I think, initialize. Pi game a knit, maybe this is not happening anymore. So does anybody call initialize here? No usages, okay. Maybe this used to get called automatically and no longer do it. Because if that were the case, then nothing we'd call Pi game a knit, which I guess is what this error is talking about probably. Let's do print, did I type printlin earlier? I've been working in Kotlin. I've been using printlin a lot. No, okay. Inside, you need a string, inside initialize. Let's see again, let's see if that prints. Guesses it doesn't. So I have a lot of extra prints right now because I was discovering that little quirk of the import order that I was telling you about. And I was doing it with prints. See, yeah, nothing ever says inside initialized. So we're not going in there. My guess is it used to get called automatically. My guess is it used to get called automatically and no longer does. Let's look in Blink-a-Display.io. I am on main, let's update, make sure we're on the newest one. I might need to step back to the old one as well so I can see what it used to be like. Is there initialize in here? Initial, no, there is not. I wonder if there used to be. Was that an override? Doesn't say override, but I guess it doesn't have to. Okay, what does this do inside of a net? Out of a net has a billion arguments. It raises some errors if they're bad. It sets some variables, calls display core. It sets some more variables. It is sending the init sequence. Our init sequence is null. The init sequence is for those displays on the spy bus. You have to call a net and create the window before you can start consuming events. Yeah, it used to do that with the old version of Blink-a-Display.io that was happening already. So this one I think some of the internals we were relying on to get called automatically no longer happen. That's my guess of refresh, refresh display. Background, yeah, see background. I think maybe this kind of stuff lies around a different thread going on. I wonder if that's gonna make us when we actually try to run fill row, please reset, auto refresh, brightness, width, rotation, those are all properties, bus, root group, okay. So we don't have any kind of like, we don't have any like private initialize. That's just not a thing in here. It probably used to be, I guess. I searched by tag or like, I don't know how to find, really what I want is version 111, or 011, 0111. I wanna see this version. I wanna get my code to this version. I guess, can I find this hash 3CC0A64? 3CC0A64. But now I have only one commit. Really what I wanna see is like the code at this point. Can I do a checkout? If I do like get checkout, that commit, will it give me the code there? Maybe search for pygaminit. Yeah, I have a knit, but it's inside of initialize. And I am assuming I need to move it to somewhere else, but I wanna see the old version so I can see what past Tim was doing. Cause I don't remember, I don't remember what I was doing. Check out revision. So I wanna like try to confirm a little bit whether or not my guess is correct as to how it was. Yeah, see, here we go. So this used to have initialize. Initialize used to get called inside of a knit. And then my pygame display was relying on that fact. You call it's initialize, which is calling pygaminit, which yeah, as the ship says here, is very, very much required for not only drawing on the display, but doing the events as well. It makes sense, cause if there's no window, there's no events, right? The events come from the buttons on the window. So let's go back to main and then I guess we can just call initialize ourselves or we can move all this code into a knit. I think I lean towards just calling it ourselves. So we'll go inside of a knit. We are calling super a knit. Should we do it before or after, I guess? Well, so I was gonna say, how are we doing it before? And the answer is before we were doing it inside of. So it wasn't really before or after. Let's go after, I guess. I feel lucky with after. Initialize, we need to do self.initialize. In it sequence, we can pass it, but it's blank. It doesn't matter, right? It's not gonna, or it's even none or, oh, it's an empty two-pole, okay. This function's not even doing anything with it, but we'll pass it to make it happy. Okay, initialize. Hopefully that's gonna be working on this. Inside initialize, no object width, do you mean width? Okay, we're getting further. Okay, so width, knit, initialize, self.width, height. Yeah, probably, I guess, right? I guess there's kind of the same thing. These probably used to exist. They were leading underscores, so they were private. They should have probably not been being used anyway. And now they're gone. Here's probably our next problem too, because core, it looks like right here, it says core doesn't exist anymore. So we could run this. We might get a window this time. Yeah, we got a window, but our refresh is not gonna be working because inside of this, it's trying to do core stuff. Is this even getting called? I'm surprised this doesn't crash, honestly. Somebody's calling refresh because we're getting all those prints refresh here. If not auto refresh, I'm gonna display force update true. Does anybody call pygame refresh? All like this. Target, display thread threading. Ooh, interesting. Where is this? This is initialized. Ah, okay, this is like making it loop. Interesting. Let's make sure this is getting called. Something must be, well, this is what's getting called, okay. That's what I'm seeing get printed. Let's make sure this is getting called. Print inside pygame refresh. Exceptions inside of threads don't crash. Yeah, I have seen some, definitely seen some weirdness on that too. So I'm actually not seeing that get printed each time. Okay, it got printed once. Oh, but I guess is it inside this loop? While not, T event is set. Okay, so I guess there is a while true in here. Basically, we put it like this, we should see it get spammed. No, we're still not though. We got more crashes. What was this stuff? Display core, okay, current group. Yeah, it looks like we got some more errors there around core, pygame refresh. I guess we crashed it, I don't know. Inside pygame, we got one right there and then threading errors. Then no longer working. Okay, so core used to be a thing in buffer. So we're gonna have to figure out both of those. What's the new version of core? Oh, there is a core. Wait, what? How come we aren't having that? Result attribute reference core for pygame display. Cause we extend display. Init has been called right here. So that's our init and inside of init, it's creating core as an underscore display core. Display core up. Oh, it's not saying it doesn't have core. It's saying core doesn't have current group. But it, okay, so pycharm seems to not be able to find underscore core. But the interpreter, when it runs, actually does find core, but does not find current group on it. I guess it's prompting me for the non underscore. My guess is that it has the non underscore one, right? What is display core? We have for properties. Set rotation, set root groups, start refresh, finish refresh, release, fill area, flip area, region, send, bus free, begin, and get with, get height. Hmm, current, what was it? Right, group. Probably got to be the current group, I guess. Area might not be, that's a group. Does group have fill area? Fill, oh, it's okay. Buffer, it's buffered, off that. What are we doing with that? Paste. Oh, wait, wait, wait, that's not, that's not display IO. That's, that's our display, where's buffer, paste? I think buffer is a PIL, because you can't call paste on anything from display IO core. Paste, I think, is a PIL method, isn't it? Listen to exist though. Just say self.buffer, convert, that's also a PIL thing. Okay, so the good news is, maybe we can just create a PIL thing. But I mean, that's not gonna help us, because we need to paste this buffer. That buffer was on display. Back to that old one again, self.buffer. Inside refresh here, it's doing the same thing, paste. It was an image. Go through the groups and add each to the buffer. Okay, let's look inside the new refresh. There must be something similar to this. No pasting. Maybe in here. Buffer size. Where do we paste to? Emory view or fill area. Not actually calling paste anymore. Uh-oh. Hmm, gave an image to buffer. Groups and add each. So yeah, I kind of just lifted this. This is basically the same code, right? That current group is none, buffer. This creates buffer here. It says current group fill area. Or in then it says self.buffer.paste. It says rectangles to this stuff. Did we run it like this? I assume it's not gonna work. They're messed up. Let me turn this printing off. So, we're done. Got an exception. We got an exception and the third got terminated since Core is added at runtime. Probably sure I will not know anything about it. I gotcha. Fill area. We're currently passing only a buffer. And then we're trying to do this, which I assume is gonna fail anyway, even if we get past this. But this does look like the code that's in the display now. Maybe we just need to do the same thing as we don't have buffer size or mask length. We already had buffer, actually, though. So I guess we're no longer using paste because instead we're using fill area. Do it for us? Is that true? I don't know if that's true. Fill area. It's past. We don't have subrecting. Refresh area. Maybe we should be down inside of refresh area. Of refresh display area. Is anything called us? Subrecting. After this stuff happens. Ah, that is not what I meant to do. Display area. Refresh area. We still have a refresh display area? We have only refresh area. I wonder if that is just a renamed version of the same concept. Loop through dirty areas and redraw that area. Okay, yeah, it is. Maybe I should cut all of this. Maybe I should keep all of this stuff, except for these. This is not gonna work though because this is relying on getting self.underscore buffer and it doesn't exist anymore. Yeah, it was pulling self.underscore buffer. That's the visual content. That's everything we're gonna try to show in the window. So if that doesn't exist, then that doesn't work. How would there be no buffer? I really don't like how it does this. It's randomly not including code. And then when you find stuff that you know is there, it's just like, nope, not there. Don't know what you're talking about. See, so we used to get this buffer, create it. It's a PIL image was. Doesn't happen anymore though. Not sure how do we get, how do we get, because ultimately I need that PIL image. Well, I guess we don't need it, but the code we have now is made to take that PIL image and send it to the Pygame output. If we don't have that PIL image anymore, we need to figure out what we can send. I wish there was a different place to see this difference that had all of these expanded. So I can't see like, okay, so we're inside refresh here. But the old one used to like do some business code with the fill area and the paste. The new one, lots of logic around time and then calls refresh display underscore. Does core have buffer? Any core has been in that buffer for a way to get access to the same thing. This core is added, oh right, this is old refresh display. Start refresh. But now it says core start refresh and then it, oh no, it says if not start refresh return false. And it says areas to refresh, get areas. For area in areas, call refresh area. Get refresh area, gets a list of the areas to be refreshed. Here we go. We used to have refresh display area, but now it's gone. We have refresh area instead. And inside of here we got, we created variable image from self.underscore buffer. Is an area, that's not what we want. Glank remaining rows, sub rectangles area. Oh boy, set region to update sub rectangle size, color space. I don't get how this works anymore. See we just don't have any of the same kind of code anymore. So we might, do we maybe not even use PIL at all anymore? I will say too, I'm pretty sure Blinka display only in the past at least only supported the Raspberry Pi, single board computers, the Linux, not the MicroPython, I believe. Due to it using stuff from PIL, which actually might just not be the case anymore. So we are probably gonna have to make our own, we're probably gonna have to make, we're probably gonna have to reintroduce the dependency on PIL and make our own image object, which is what the buffer was. And then we somehow need to copy the data from the display IO object into our PIL image and then we'll be ready to send it to Pygame. And at the very most inner part of this onion here, there must be a thing that's getting the color of the pixel inside the bitmap. Cause at the end of the road here, where you effectively have a bitmap, there must be something in here that is grabbing the colors out of each pixel in that bitmap and sending it, which is like we can hijack it sort of at that spot. And instead of sending it there for that pixel, we can send it into an image object. Then once we're done with that, we'll be ready. But I don't know what fill area call the current groups fill area. I don't actually know what fill area does off the top of my head though. Why install Blinka for MicroPython instead of just running MicroPython? It would be because you want to use CircuitPython libraries. So let's say you want to use a CircuitPython driver library for a particular sensor or piece of hardware or whatever that might not work out of the box on MicroPython because it doesn't have like bus device the same way and the different stuff like that doesn't work the same. So if you wanted to use a CircuitPython library that's written for CircuitPython, then you need that sort of adaptation layer. Is this display? What is this? This way. I think fill row actually gets a row, right? Yeah, extract the pixels from a single row. So this, yeah, the name is weird to me, but it takes pixels out of one row of pixels from the bitmap display, which ultimately has a bitmap inside of it or whatever. I don't know if it's literally a bitmap object, but it's two dimensional grid of pixels. So it's a bitmap in my mind. So if you want to get all the data from a display, if you want to get all the visible data from a display, what you do is loop over zero to the height of the display for each one of those values you call fill row, passing in your loop number, and then we need a buffer for each one. And each time we call this, our buffer right here is gonna get filled with one row of pixel data. So then what we would wanna do is take that row and append it into a, I mean, ideally, into a PIL image object, in our case, but you know, whatever, you would keep appending it into something until you have a full thing. And then when you're done, once you get to the end of all the Y values, you now will have some representation of whatever was on the display. Okay. So, but it's not in a loop. Oh, well, but that should be one row at a time. I'm not following this. So refresh area takes an area, which if I am correct, I think this is just a rectangle. I think area is basically like X, Y, width, height. Let's validate, right? Area. Area. Oops. I don't know if this is, is this installed or in 3.10? What is this thing running? I don't know why this is running. I thought I had a virtual environment. I should not have just done that. I don't know what the problem is now. We're back to like a completely, totally entirely unrelated problem. I don't know how to get back to where I just was. Do that. One, oh, oh, install one, oh, oh, yes, so. Just easily change one thing and run it again. We tried to import terminal IO and then it failed with ont. 10.5 image load. There's no such, even is that a thing? IO resources. There's the virtual environment. I'm not using, I swear we had a virtual environment for this the other day. Okay. We run this again. It fails because we have no pie game or anything that we need anymore. Okay, slowly, but surely we're also missing display tasks. We need to get that. How to install, okay, getting further. So I think I had installed version 1.0. Oh, I guess I should have never done. I should have just always been working towards the new one, but I think I started taking a steps backwards. I eventually was on version 1.0.0. Now I've installed the newest one. We still have different errors unrelated to what I was actually doing before, I mean auto refresh. Yeah, probably I guess, right? Oh, no, I mean, this is inside display. It's just like internal ground calls background and then auto refresh. No attribute auto refresh. Yes, I don't know. Not really, but I mean, it does right here. It has an auto refresh. I would not have an auto refresh. I mean, it has auto refresh with no underscore also, but it does have the other one too. So, we don't have buffer size or mask length. I don't even know how do we fix this because this is internal. We have redefined properties for this. I don't know, that's weird. I'm pretty sure it does. Obviously, I actually have auto refresh because we see it in a bunch of places. I don't know why that thing crashed. Let's see, maybe if we just fix this, if it, how solves our other issue. Was doing buffer size and mask length. I have no idea what buffer and mask are, conceptually. Where does this come from? Oh, where did my area print go? Did we actually get our area? That's where we really, no, where was that area? Clipped, is it gone? Or is it inside refresh area? Is this in the code that we're using? Oh, because I installed it from requirements, which installed it from pip, not my local edited instance. That's annoying. So it's getting the wrong version when it installs it this way. And now because it has the wrong version, it can't, because it has the wrong version number. It's not even getting the wrong version. It's getting the wrong version number. It doesn't know what version it has. Thinks it has 0, 0, 0, 0, 0. So then it's like, display text requires 0.10.2 and you have 0, 0, 0, 0, 0, 0. So you can't use that. But the truth is we have something newer than that. It just doesn't know, just a little bit. I don't know how we fixed that. You know, I could change it here. Feels like cheating, take it. It does feel like cheating, but take it, I guess. So we should be able to see our print now, hopefully. We're still gonna crash because we don't have buffer size or mask size. I don't see it, nevermind then, I guess. After made display, why is this not? Why is there no new line here? Good pie game refresh. And then there's after made display here. I don't know why those don't have a new line between them though, because it doesn't matter. We do still have the same problem here where it doesn't have auto refresh, and I don't know why. And then we do also still have the problem here where we don't have buffer size. Used to be clipped. How come we don't print our area? Trying to step through this code. Why did it not print this? Printer string? I don't think it'll matter. I could put a new line in it, and it might help it. Honestly, my true guess is that it's like, threading, they're getting printed on the same line because they're getting printed from different threads. Is my real guess? Okay, the first thing is gone, which doesn't make any sense because I didn't change any code related to it, actually. Right, did I change any code related to that? Didn't, did I? But still just doing, did I? No, I don't know. I can't see the old one anymore, which is convenient. Thanks for that. I don't know. The first error's just gone. Okay, whatever, I guess. Let's just go with the next one. Maybe it'll pop back up eventually. So I think we need to calculate more of this stuff. We probably need to pull more, probably need to pull more of the code from the new refresh area. In fact, I'm kind of thinking we probably just want to gank this entire thing and try to be remaking this essentially. But the sub rectangles add more layers because this is expecting an area, whereas the old version of Pygame Refresh was refreshing the entire display and looping, handling these inside. It's no longer doing that. But this says refresh area, but it is still looping over sub rectangles. Why would it have multiple sub rectangles in one area? If this does refresh area on one area and if one area represents a rectangle, there's what I was trying to prove with this print statement actually forever ago before we went down the last rabbit hole and still haven't really come out of it yet, which is still not getting printed actually. I guess we're crashing before we get there. But we're not, I guess we are, I don't know. I guess we are crashing before we get there. So if I just stubbed this, then we could at least run and maybe I could see, maybe we could validate what I set out to do forever ago. Maybe we can validate that area does represent a rectangle. We just give that the old passeroni action. So it's not gonna work, but maybe it won't crash. Yeah, though it doesn't work. It doesn't crash. And it also doesn't ever print this. It makes it pretty hard. You tell what an area is. Why would we never call this? This gets called by refresh display. Guessing also doesn't get called. It gets called by the refresh display inside of refresh. Are we calling super? We're not calling super. Okay, that's why we don't ever see our print statements. But I mean, we don't really want to call them, I guess. Do we, right? What is all of this stuff? Refresh has frames per second stuff. Yeah, we're not really in control over any of this logic. I don't think in Pygame display land. It calls refresh display, which in turn finds all the refresh areas and calls refresh area on each one of them, which in turn loops over sub rectangles, which here area is a rectangle because that's what's creating right here. So how do we get from one area passed in to refresh area to then having multiple sub rectangles? We have only just the one area that we were passed remaining rows. That sounds like our logic for the fill row, which was taking one row at a time. I guess is what this was doing. Okay, so this was calling fill row and putting it into one or the other of these. I guess buffer probably, I don't know what mask does. I mean, I suppose the truth is technically we don't really wanna get inside of here because we're not gonna wanna do this because we don't have a bus. All this stuff is gonna fail that's trying to actually send stuff on the bus. So maybe Pygame refresh could call, but then this is just, well, it could be refresh display. So that will actually call it. So we can hopefully finally see our print statements for area. No, okay, yeah, but we can't concatenate area in string. Okay, well, can I just print area by itself then? Really, I wanted to just know what area it was, which I guess we kind of figured out a different way at this point, but okay. I don't know what TL and BR mean, but I mean, that's like x, y coordinates and then that's like width height. Oh, top and left, top left is zero, zero, bottom right is 400, 300, there we go. Okay, so that is definitely a rectangle. Currently represents the full size of our display, but I do think we don't really wanna call this. Really, I think what we want is we want to, we want to basically make a fake one of this, but the problem with that is this was not getting called until we added a call to here. If I made a new one of these, will we be calling the overridden one, which would still crash the same way, but would print that, which implies we can then start, why did that take forever to run? Did I change that? I changed the imports again from area. Yeah, I have to be careful with imports. The imports are randomly, yeah, see, we can't, basically we cannot import anything from display.io before we import PyGame. If we do, then it makes the PyGame import take forever. Not forever, but a lot longer than it would otherwise, and it didn't seem like it was as long as we were seeing the other day. Okay, yeah, yeah, yeah, yeah, we're calling the overridden one. Okay, so now if we in our overridden one could change the logic, so let's let it do everything it's doing and then it is basically building a buffer. It's basically building a buffer with each, because fill area, no, that was fill row. It was fill area. Oh, I go to the one inside tile grid. What's this doing? Where did our fill row go? I don't think, I'm not sure if this will be human readable. It is, and we get something that looks like pixels, then, okay, we got this thing again. Did finally get this. There must be a time thing going on too, like must be race condition, I guess. I feel like we maybe have the same problem here, because this is saying it has no begin transaction. Do I mean this one? But this is inside of the core, and it does have a begin transaction. It's calling it from the one with no underscore, so it is calling this one, which in turn is calling this one, so it's can't be that we didn't mean that, but maybe it's just not initialized yet. What can I slow it down? I think we're calling refresh before the display is actually initialized. Where's that refresh thing again? Pygame.display thread is what's looping pygame refresh, which is now currently mostly commented out, but calling refresh display, which is ultimately calling back to refresh area. We're doing this inside of initialize. Okay, is that happening before stuff exists? Press display, so we get back to here, so then it still is saying now inside of set region to update rectangle, it tries to call begin transaction with no leading underscore. As we know, then turns around and tries to call begin transaction with a leading underscore. Maybe we can just not do that. Did it print the buffer? Did we make it to there? There, I'm just gonna turn these off. Basically though, like here, buffers should contain ultimately data that represents some amount of pixels on the screen. I was thinking it would be one row, but it's probably not, since this is using fill area, not fill row, probably not just one row. It's weird though, because it does say like remaining rows here, which makes it seem like it would be rows, I don't know. Anyway though, if we, whatever it is, whether it's a single row or not, if we could get all of those things that come from refresh area being called multiple times on all the different areas, if we could get all those buffers together, we should have all the data that represents the visible stuff on the screen. It should be possible to somehow take that and get it in a way that PIL will recognize it as an image, which is ultimately where we need to be get it into high game. So it's calling set region to update sub rectangle, which is then, yeah, I don't think we want this at all. I think we wanna stub this. This looks like it's like, it looks like it's, I guess the displays must have a functionality where you can tell it like, hey, the next command I send you is gonna be the data for this rectangle, you know, this sub rectangle. My guess is that's what this is doing, is it's saying, it's like letting the display know, hey, I'm about to send you some data and that data is gonna represent this rectangle. But in our world, we're not doing that. Our pie game window, we aren't really messing with different rectangles, we're refreshing the whole thing at once. It's slow, it's inefficient, but it works and that's at least how it used to work. And I think that's what we wanna get it back to, but yeah, that's tricky because this is on core, but it's inside of here, we can just comment it out though, right? What is all of this stuff? We need these, these don't get used. Why do these exist but not get used? That's weird, I'm commenting all of this. I really wanna get a look at buffer to validate that it is containing some visual data plus free, that's more bus stuff. Okay, so that's memory. Can I get a, I don't know what that is, memory view. Can I iterate through that or something? Can I somehow look at those? P-I-L make image from memory view, memory. But this only represents one, really that's not gonna work, right? This is image file, but that says string IO. But this says image.value, what is that? I don't know what that is. I don't know, that doesn't look like it's a memory view. Image.value, I don't know what a memory view is. From buffer, that sounds promising. I am one, image two bytes. Okay, so what if we tried image, print, image equals image.from buffer. There's so many arguments, I have no idea what the correct answer for any of these are. I mean, I, mode, I guess, is probably I to match this. Maybe I don't actually, that's true or not. Then it wants a tuple with size, I don't know what that would be, I don't, what is a memory view? I don't know what a memory view, what functions does it have? What can it do, print, dir, buffer? Can I get the length of it and the length of each row? Is it like a two-dimensional grid? Two-dimensional array, I mean, it applies. Got that auto refresh thing again. Release, read only, invites, item size, format, shape. The shape give you X, Y size. That still says memory view though, I don't think that's gonna be anything different. Or it's gonna be memory view instead of memory, I guess. And we got 6,000, 60,000 and zero. Oh, but that's, no, I don't know. I don't know where that came from. We have more bus stuff going on too. I need to figure out if this buffer is full display or what and what it is. Could just hard code this for now, I guess. Data, decoder name, data is buffer. So do image.save, I don't know if image data. That's probably on our from buffer, right? Yeah, that didn't work, right? I'm not convinced fill area is what we wanna do. Core.fillarea, I don't think that's, I'm not sure that's what we wanna do. Yeah, this is a lot trickier. I did not realize the extent to which the new version of Blink-a-Display.io basically has gotten rid of the usage of PIL under the hood. And in my case for the Pi Game Display thing, that's actually making it quite tricky because the Pi Game Display really was built on the idea of that internal PIL image being super easy to, I basically took that internal PIL image and passed it to the Pi Game window. There's a little bit of conversion between the two, get the formats right or whatever, but that's basically all that thing was doing. And now it needs to be also somehow creating that image which requires understanding more about how this is working internally, which I do not. What do we think sub rectangle is? Let's see if sub rectangle is 400 by 300. Yeah, it is. Whoa, I accidentally ran the debugger. Not easy. Not a debugger, as you can probably tell by the million print statements I've added today. I'm a print debugger. I see, it seems like sometimes we just get this. This is like a race condition thing. That does represent the full screen though, that's good. So fill area is getting, that buffer, that memory view probably does contain the data we need. I just don't know how to get it from that type of thing to, oh, there is two bytes right here, should we have done that maybe? But it was calling from bytes and then I guess it would have probably called two bytes. Whoops, there's from bytes. But it takes the same arguments. I don't think it's gonna make a difference. So I'm sure that was already happening. It does also do this, which is interesting. I don't know why it's doing that, not enough image data. Is it actually saving? I never did actually look to see if it saved a thing. Guess I kinda doubt it, but yeah, from bytes it's failing on not enough image data. So the crux of this is, what is fill area returning from core? And then how do we get it from that format to what PIL is expecting? I gotta run to the restroom real fast though, so I'll be right back. Okay, back, area clip, what does fill area do? Call the current groups fill area function. If current group is not none, return current group dot underscore fill area, which takes an area, a mask, and a buffer. And I believe group basically is trying to find a tile grid and eventually it's gonna find a tile grid and then it's gonna call fill area on the tile grid, which is kinda like the end of the line. It'll call it on the group and the subgroup and the subgroup and the subgroup and eventually at the end of the line it should find a tile grid. When it does, it calls it on the tile grid which says draw onto the image. Biggest, is there a fill area? This is doing a lot of stuff. Transforms, lot of math, input pixel struct. The pixel, how, why? Input pixel dot pixel equals bitmap dot get pixel. That's basically pulling a pixel out of the self dot bitmap, returns full coverage as a Boolean, I think, but I have no idea what presents really. So I don't really know what fill area is supposed to do. The comment doesn't really tell me enough to understand this, draw onto an image. This one is the one that's in tile grid, which is ultimately the one that gets called because when you call display core fill area it calls current group fill area, which will loop over all the things in the group until a calling fill area on each one of them and eventually one of the ones that it's finding is gonna be a tile grid. That's gonna then call into here, which is then doing all of this stuff, draw onto the image, but I don't know what like the mechanism is. We do have input pixel struct and output pixel struct. We are looping over, I mean, it looks like we're kind of doing our rows here. So we're back to maybe seeing fill row potentially, but we're not pulling it by row, we're pulling it by pixel from the bitmap. I guess is that we're writing our data into buffer. Maybe I don't know how pack into works off the top of my head really. Is that packing stuff into buffer? Cast B, he's doing cast B. So fill row just turns around and calls fill area. We have to get fill, I was thinking for a moment, maybe we could try fill row instead. That does us no good, because that's just gonna get us right back into fill area. We have to figure out how to make fill area work and I don't know how, so I might be kind of stuck. I may try asking Melissa, she knows how I could get the data out of the display and back into a PIL object. We get the data from display. We will see, I don't know when or if Melissa will be around again, but I will ask over there, because I think she'll have a better idea. Double buffering, possibly? Not sure the context for that question, double buffering. If that's like, if you're saying, is this using double buffering or if that's a way to solve the thing I'm asking about? Not exactly sure, in either case, really truthfully. So it's a tough puzzle, yeah. I think we're close, like I'm pretty sure this buffer contains the data we need. It's just not in the format we need it in right now. That's kind of my, well, after we call this, I guess, like just right here, it doesn't contain any data yet, but then once we call fill area, if I'm understanding right, I believe it is filling that buffer with the area from the sub rectangle, but then I don't know, so we had buffer, so shape said 60,000, so that's just one, that's not a 2D thing, that's just a list, basically. That's one array. I'm gonna use Blanka from Pyodide. Oh, interesting, Pyodide. That's the, is that a web? Integration layer or something, right? That's letting you use Python in web context, like in JavaScript or from HTML or something. I have enough image data. How can I print a memory view? Python print memory view. Wish I could get a look at this to try to discern if it is data from two bytes. It's gonna print a byte string, but that's really not gonna help me that much as it print something different, but I don't know if the string is not gonna be legible. Oh, maybe it is, okay, hold on. Oh, seven EO. Okay, yeah, yeah, yeah, this is our color. I'm pretty sure this is our color data. The thing it should be drawing is one outer rectangle of one color, I think purple, but I'm not sure. One inner rectangle of a different color, I think green, but I'm not sure. And then some text in the middle, and the text in the middle is like, it's constantly shifting colors actually right now. So this 07 E0, this right here, I think, is our outer rectangle color. And then if we scroll enough, we'll find our inner rectangle color. Somewhere in here, like our XA811, this is our inner rectangle color I hypothesize. Eventually in here, we'll find our words. So it's written like hello world or something. So I have some pixels in here that represent the pixels in the letters. Lots of that stuff mixed in. We'll have lots of our still inner color going on here from our inner rectangle. And then eventually we'll get past the end of the inner rectangle and we'll be back to having the outer rectangle, which is our E070 or 07 or whatever. So that is, I'm pretty sure that is our data. That I think is our image data. I just don't know, how do we get it back into, how do we get it back into PIL? What format is it in basically? It's in a format that the, it's in a format that the spy bus is expecting it to be in. Maybe we shouldn't have used I. Maybe I should have used L when I did this. Why is this passing L again actually? XBM RGB. So if our buffer data happens to be in this, whatever this format of XBM RGB is, if it happens to be in that format, and it should actually work, I don't know why it passes L again. Maybe we should try L. Feels like a shot in the dark. Okay, it didn't crash anymore. And it saved this. Okay, it's not quite, but it's kind of there. We got, it's not nothing. The mode is probably wrong. Mode L, that's probably wrong. But this is actually really close. What are the possible modes? We just try every mode. PIL, image.modes. What are the possible modes? Python and the browser. Okay, nice. I was thinking about that at one point using that with Blink-a-Display.io. I was thinking it'd be interesting to have a similar kind of thing that I made, like this thing is outputting from Blink-a-Display.io to Pygame.Display. I was thinking it might be cool to output to a canvas inside of a browser as well. So I is 32 bit signed integer pixels. F is 32 bit floating point pixels. L is eight bit pixels grayscale, which is not what we have probably. Eight bit pixels mapped to any other mode using a color palette. Let's try that. Why not? P, sure, give it a try. It is still gonna crash when it tries to do bus free. And we do still occasionally get this race condition where it prints an error about that, but it doesn't seem to be crashing. Yeah, this bus free one, this is gonna keep happening until we fix the next thing. Can I say, do we get, did this, I don't know if this changed at all. I'm gonna delete this so that we can actually make sure we're looking at the latest one. That is still the same. Just anywhere, color palette. Three X eight bit pixels true color RGB. Is that what we use? I don't know. I think the thing is this newer version of Blink-a-Display actually allows a couple of different ones. Is it just RGB? If we get to the point where our temp PNG actually looks correct, then we're like really close. Got not enough image data. RGB doesn't work, not enough image data. I mean, then it can't be RGBA, it can't be CMYK. I don't think it's very likely to be this. I don't know what this is, but I doubt it. I don't think so. We already tried I and it wasn't working. Our variations of I, I guess. Let's try to fix our bus free thing. So P worked and L worked, both P and L worked. P, I think would be the one that would give us color though. I'm curious if this would get called twice. This feels like it's the entire display, right? This feels like it's the whole thing. I'm expecting that we would not get, like we would get called again only next frame. I'm expecting that we would not get called again with a different sub rectangle until the next refresh happens. If not bus free, let's just do, skip that. We don't care about bus, not using a bus. Where we're going, we don't need buses. It does not get called again. It only got called once. And interestingly, it actually does not appear to be getting called over and over again. Which is kind of weird. I figured the auto refresh would be calling it over and over again, truthfully. That does not appear to be happening. Do we need to somehow use mask also? I don't know what mask is for really. Do we get any difference? I mean, surely this makes no difference if we use from bytes and then call two bytes, right? That's gotta be the same as using from buffer. Did that, was it purple for a split second? Did I see a purple in there? Hey, this is now all black. Hold up, hold up, hold up. Is this all black? And didn't I, maybe it was a trick of the eye, but I'm pretty sure I caught a split second of purple. Purple is the actual color I'm expecting to output. Why was it all black then? Offered out, I did it finish. Oh, I'm running the wrong thing. That's not what I've actually seen. So this looks messed up by the size, like the text should be in the middle and the text should be smaller than the width of the inner rectangle. So, and we do get the same thing. Like this doesn't make a difference. Is there any reason to think any of these other modes are gonna do us any good? I really don't think so. If we could figure out which color mode this represents the correct color in, maybe we could go from there to knowing which mode is appropriate. I mean, we don't really have any other, very many other options, I guess. You saw that I didn't work. I think it's very unlikely that F is gonna work. I'm not using floats, not enough image data. So that leaves us with these things. L with alpha and L works, but L says it's gray scale, which is not really what we want. Ultimately, we're trying to get color. That's probably not what we wanna use. Why is L used inside tile grid? No, it's, oh, that's struct, Pat. I think an L was used in here. Where did we see the Ls? I copied that from somewhere. Where was that from? That's casting top, aye. That's cast, that's memory view, aye, which is different than, that's not PIL anything. Where did I get that L from? Did I just guess L? I swear I saw that from somewhere. What does memory view cast do? Indianness, I think Indian to the little Indian, maybe. And this has already taken effect for us because we have the same logic here. It can't be L. It has to be either P if we had a palette, which like we probably should. Honestly, that's probably what it should be. Real options are like I, one of these ones maybe. We don't use 16 bits, do we? 16 bits, that's two bytes, I guess. We have two bytes. Maybe that's what we're doing. Okay, let's try this. Let's try a couple of these aye ones. Let me delete this. Okay, that is much closer to what it's actually supposed to look like. The colors are wrong slash don't exist, but the like pixels are right. Is it the inverse? It's not the inverse though, because we should have purple and green, not black and white. Okay, let's try one of the other ones. What do we have? Integer, unsigned, indian, let's do this. We're getting, I feel like we're getting pretty close now. Yeah, I feel like we got to be getting pretty close. Should have deleted this again. I'm pretty sure it does update, and not write it as a PNG. That's unfortunate, which not loaded. We bunged it. I'll try bitmap, I guess. Don't really need to, ultimately we don't need it to be a PNG. Doesn't want to do that either. Unsigned integer pixels. We could probably put this back, we put that back to PNG probably. I think we should probably put that back. How's it going, Bot Zerker over on YouTube? I will say I'm probably getting pretty close to wrapping it up here. I'm gonna try a few more of these because I feel like we're pretty close, but not quite there. 16 bit unsigned integer pixels. Maybe this, BGR, I feel like I've seen this come up somewhere else. We tried RGB and it didn't work. That's an eight pixels only. I mean bits, unknown raw mode. I mean 16 bits, it must be one of the 16 bit ones. If it's gonna be anything, it must be one of the 16 bit ones because that I, 16 one, that gave us the correctly shaped image, right? This one here gave us an image of the correct shape, the colors were messed up, but the size and shape, and I swear I just saw the purple again, I thought. Seein' things? Like for a split second, I feel like I saw the purple in there. I don't know though. Yeah, see, this is so close to what we're trying to actually draw. So I tried 16L, I tried 16B, I'll try 16N, but the other one's failed so I don't really have high hopes for that. I think it could be 24 because like 16 we think has gotta be the right number of bits probably. Otherwise I think our size would not be correct on that one. This one can't create the image anyway. Wow, so close, 16 bit unsigned integer pixels. I wonder why BGR16 doesn't work, known raw mode. Do you know if Adafruit will update the eyes bonnet software to work with Raspberry Pi 5 assuming it needs updating? I do not know the answer to that, I'm sorry. I would presume if that's a library or a project or something that's in the learn guide system. If it needs updated for the Raspberry Pi 5 I would assume it will eventually, keyword eventually get updated to run on the latest stuff. I have no idea the availability of Raspberry Pi 5s, if it's anything like the Raspberry Pi 4s were, which is to say not very available very much, then it might take it a while to get updated because it won't get updated probably until someone has hardware. But I'm not really up on the Pi 5, I don't know if those are available yet and if so are they readily available or is it kind of the same thing going on as the 4s where it was really difficult to actually get? And I don't know, I don't know what that project is, eyes bonnet, I'm not sure what that is. Yeah, if it's like a learn guide or a project or something that's in the learn system and if it does need to be updated for the Pi 5 my assumption is that it would eventually get updated. Pixel uses the full range of bit depth so one bit pixel has a range of zero to one eight as a range of zero to 255, 32 has unsigned integer pixels. Okay, remultiplied alpha. Let me look at one more thing and then I do think I'm probably gonna wrap it up here. Ah, this is not searched up anymore. Oh, okay, this was in the other repo. Geez, correct all of this. That's a bummer. Interesting that I'm sure it was great chilling with you all, maybe you or we can be full breakthroughs and whatever and whatever you choose. Yeah, thanks for that, I love the factory. So I'm pretty sure I had just lost any changes I had inside of here. I don't know if we had anything that was making it work anymore because I've tried too many things and I don't know what's what anymore. But I went to check out and then because I checked out I had changes and so then it was like wanting me to merge. What I really wanted to do was just bail, undo, go back to the state I was just in literally right before I clicked that thing. That's what I wanted to do but it doesn't like to do that very much. It wants to do rollbacks and undo code changes and stuff. So we probably lost, I guess what we lost was a bunch of prints, I think. So this is saying self.buffer.convertRGB. This was an RGB before. How come image equals image.convertRGB? Surely that's not gonna work, right? Where did our color information go? I feel like RGB must be the correct choice based on the fact that I16 is giving us the correct shape and size. That makes me think that 16 bits is the correct number of bits. And then like here, BGR 16 bit reversed true color or RGB, that says 3x8 bits, that'd be 24, not 16. So is 16 actually right? 16 is not actually right. Interesting, this time I got all white except Hello World is very, very faintly gray or something, but definitely not RGB. So two bytes takes an order. What is that order? Is there any chance we have just the wrong order? Cause this says unsigned integer, but then that has options for little Indian and that has options for big Indian. Neither of those worked. But when we call two bytes like this, it can take an order. Order can be CF or A, C for Fortran order. A is exact copy. What, when the order is C or F, the data of the original array is converted to C or Fortran order. Okay, that's not the order I was thinking. I was thinking that was order as in the Indianness. That's a different, that's a different order. I don't think that's what we want. Backwards or something that I would assume. Make sure that sub linker display seems more complicated. Well, I mean, it got more complicated with the recent update than it used to be for sure. I don't know for sure, but it seems like to me, but that's because it's now doing more of the stuff that the core display I was doing. So it is more complicated now, but it does more stuff internally. Like I think the whole sub rectangle thing used to not really exist. I think there used to only really be the one rectangle. See, I don't know. When we call this with either C or F, we get blank, all black, 0, 0, 0s on all of them. Assuming that should be the same. But when we do two bytes without any of those, we got our picture just around colors. Oh, F, okay, so F gives us this one. So F must be default, I guess. And A and C don't help us. Well, I don't know, we're really close. I feel like we're basically down to just, we need to figure out the right format to tell it the image. Maybe possibly we have to like load the image and then convert it after the fact or maybe mask. I was getting ready to print mask. Maybe mask contains some part of our information. Like maybe somehow our colors are contained inside mask and we need to be using it. No, mask is mostly Fs and a couple of zeros. Oh, that's our, that's a buffer. Is it? Yeah. Mask is like all Fs except for a couple of zeros at the bottom. That definitely does not have our image data in it. Somehow we just got to figure out what that format is and how do we change it up to where PIL is gonna recognize it as RGB. But I'm gonna call it an evening for now. Thanks for hanging out, folks. I will be back tomorrow morning for folks that don't know Saturday morning at 10 a.m. central time. That is when I typically stream over on my own channel. Like I said at the beginning, I'm pretty sure, although don't hold me on it, I'm pretty sure Scott should be back next week to the best of my knowledge. So he'll be back in this time slot next week, I believe. But again, don't hold me to it. Don't hold him to it. It's possible I'm wrong, don't know. And then I'll be back tomorrow and then I should be around next Saturday as well for my normal stream time. So take it easy, everybody. I hope you all have a good night, a good weekend, all that stuff and have fun. I will see you soon. See you, bye.