kovidgoyal / kitty

Cross-platform, fast, feature-rich, GPU based terminal
https://sw.kovidgoyal.net/kitty/
GNU General Public License v3.0
24.56k stars 982 forks source link

background image #163

Closed rtorresware closed 4 years ago

rtorresware commented 7 years ago

now that we can show images in the kitty terminal (awesome), can we get support for background images?

kovidgoyal commented 7 years ago

Not something I care about, personally. But it should be trivial to implement. Patches welcome.

coxley commented 6 years ago

Would love if this had a toggle for current wallpaper + dim. Only feature keeping me from kitty full time :)

teratorn commented 5 years ago

+1 need Fallout PipBoy-terminal background image support... going fully transparent against the desktop wallpaper results in horribly broken anti-aliasing and looks like garbage on MacOS.

(On a side note - defaulting to pure-black background hurts peoples eyes. You can't focus in to a black-hole, and you should have something for the eyes to focus on if you want to avoid eye-strain... a nice grey would be a better default, imho)

If I find time, I'll try to work on this. Are there PNG and JPG image loaders already in the codebase?

Luflosi commented 5 years ago

@teratorn #1005 describes your anti-aliasing issue. I agree that the black background color is not very good but I'm not sure it should be changed since this is personal preference and most people will probably change it anyways and might not like a different default color any more than the current one.

kovidgoyal commented 5 years ago

@teratorn kitty already links against libpng and uses it for its graphics protocol. just search for the png headers to see where and how it is used. As for staring into black, I strongly disagree. I find it very relaxing for my eyes. Not to mention that monitors displaying a black color are not black holes, they still emit light.

giftig commented 5 years ago

I've just swapped to using kitty today and I'm loving it; much better than all the other options I've tried in linux. I came here to ask for exactly this feature since I like having pokemon images in my terminal like a nerd.

If it's something you think is trivial to implement @kovidgoyal I'll gladly have a crack at it sometime soon if you can give me some vague pointers about where to be looking in your codebase to build it. I'm a python dev but not a C dev so I might scratch my head a bit if it involves much C.

kovidgoyal commented 5 years ago

Well, this will definitely require a fair bit of C code. You will need to load the PNG image usingthe same code that does it for kitty's exisint image display capabilities (see grahoics.c) and then display it in the kitty graphics shader in shaders.c. This will all be C code, I'm afraid.

jason81474 commented 5 years ago

I need coins

dagadbm commented 5 years ago

will this ever be done?

disruptek commented 5 years ago

What about an option to specify an alternate background color for inactive windows? I'd like to use less opacity on my inactive windows, to let backgrounds show through... Did I miss an option to do this?

sukima commented 5 years ago

@disruptek that question is not related to background image. Please open a new issue so we can keep this thread focused on one specific request (all be it a pipe dream at this point).

fenetikm commented 5 years ago

@disruptek you can partway do that if you run inside tmux and set the colours appropriately

miaXcova commented 4 years ago

I have been trying for hours to enable backgroundimage=yes in both the .ini file and using the registry. Can someone tell me how to enable this feature? If this feature is not yet available, how is the screenshot on this page possible?

SolitudeSF commented 4 years ago

@miaXcova this is a wrong kitty. here's the one you're looking for https://github.com/cyd01/KiTTY/

ctrlcctrlv commented 4 years ago

I did a bit of looking into this, as many have asked for this feature, and while I think it's cool, I don't think it essential. But I'm happy to work on something many people want as long as it's cool—after the failure of #2259, I think it best I work on things that will actually be of the greatst practical benefit for Kitty's users.

After all, my work is supported by grant.

Obviously, as @kovidgoyal mentioned, the best way in is through the existing Kitty graphics protocol. So I dug around in graphics.c. I think actually have everything we need already, if it were to just be done with a particularly clever Python script.

The icat kitten is a good starting point. Just use IM to turn down the opacity of input image, draw it to screen (center vertically), make sure that z-index is negative, and draw your text on top. Hook screen clearing event to do this again whenever needed.

One missing ingredient I can think of is that z-index is only defined as "integer" - this is a big problem for interoperability if we want to put an image at the bottom below which no image can be drawn, because I don't know if it's a Python integer, or if it's a C integer, if a certain size is guaranteed, and the spec doesn't specify.

However, once we clear up that problem...I don't think C is really necessary...

kovidgoyal commented 4 years ago

You cant use the graphics protocol for this, as that means doing a clear would delete the image. The background image has to be stored specially not as one of the graphics protocol images.

ctrlcctrlv commented 4 years ago

Hook screen clearing event to do this again whenever needed.

If it's not possible to hook this, it would need to be made so

kovidgoyal commented 4 years ago

On Wed, Jan 08, 2020 at 01:59:24AM -0800, Fredrick Brennan wrote:

Hook screen clearing event to do this again whenever needed.

If it's not possible to hook this, it would need to be made so

There are many such events, as well as many ways to clear images in the graphical protocol as well. It should certainly be possible to tag an image as "undeletable" but this seems like a hack. It means that onehas to make sure all the various ways that exist of deleting images dont delete the special image, search graphics.c for clear and delete to see how many there are. And there there is the whole alternate screen thing. One has to make sure the background image remains visible when swapping between main and alternate screen.

Why not just have a special slot for a background image? I know you dont mind writing a bit of C :))

ctrlcctrlv commented 4 years ago

I don't mind :)

I guess I'll just add it to GraphicsManager struct, then allocate it as needed in grman_alloc (based on something I'll probably add to global state)...the type can be Image *...the thing is scaling, not sure how I'll do that, as the example code uses ImageMagick. And surely some users are going to want tiling and not scaling. And I also have to think about DPI

kovidgoyal commented 4 years ago

On Wed, Jan 08, 2020 at 02:23:06AM -0800, Fredrick Brennan wrote:

I don't mind :)

I guess I'll just add it to GraphicsManager struct, then allocate it as needed in grman_alloc (based on something I'll probably add to global state)...the type can be Image *...the thing is scaling, not sure how I'll do that, as the example code uses ImageMagick. And surely some users are going to want tiling and not scaling. And I also have to think about DPI

Yeah there are a lot of details to consider, which is why I never bothered with tackling this issue. You also need to consider what a background image means w.r.t. multiple windows in a tab, tab bar, borders, margins, padding, etc.

ctrlcctrlv commented 4 years ago

Clearly many people want this, so I will have a draft PR by the end of the week.

I'll let you know if I think a choice I make could be controverisal before I make it so no code is wasted.

There's a lot to consider but it's worth it! Towards making kitty the most complete terminal. alacritty, termite etc move out of the way, there's a new king in town :crown: :joy:

kovidgoyal commented 4 years ago

On Wed, Jan 08, 2020 at 02:40:13AM -0800, Fredrick Brennan wrote:

Clearly many people want this, so I will either have a draft PR by the end of the week.

Cool, I look forward to it.

I'll let you know if I think a choice I make could be controverisal before I make it so no code is wasted.

Am happy to discuss if so. And am also happy to make fixups in your PR myself if needed, so you wont have to revisit the code. As I did for your OpenType PR.

There's a lot to consider but it's worth it! Towards making kitty the most complete terminal. alacritty, termite etc move out of the way, there's a new king in town :crown: :joy:

There are other terminals besides kitty!! Who knew :))

miaXcova commented 4 years ago

Would this mean that this KiTTY would be a fork of the other KiTTY from a certain place in time, or do both share the same base code with minor changes?

Would the real Slim KiTTY please stand up... please stand up...

Also, to clarify what I was hoping for with a background image (image or solid color) would have been to change the opacity so that the background transparency could be controlled while the text and window frame remained at full visibility. The way transparency works now is it changes the entire window text, frame and all. It's just nice to be able to see through windows when most of the window will be black anyway. Rocking 6 monitors and still never enough screen real estate.

ctrlcctrlv commented 4 years ago

We have nothing to do with the cyd01 kitty.

Background opacity isn't really on the table here. The image can be made opaque...on Kitty's default background, usually black. We'll have to figure out background opacity in another issue, this really is not the place to discuss it.

kovidgoyal commented 4 years ago

On Wed, Jan 08, 2020 at 04:44:35AM -0800, Fredrick Brennan wrote:

Background opacity isn't really on the table here. The image can be made opaque...on Kitty's default background, usually black. We'll have to figure out background opacity in another issue, this really is not the place to discuss it.

Actually, kitty already has background_opacity (see kitty.conf) no reason it should not apply to the background image as well.

ctrlcctrlv commented 4 years ago

Would be good if people actually read the config file before claiming kitty doesn't support something :wink:

I need to stop taking user complaints at face value.

miaXcova commented 4 years ago

Sorry for not clarifying in my previous post. I wasn't complaining, but asking if something was possible. I read and tweaked the config file and found an earlier version where the background image was working, but the opacity setting worked only for the image and did nothing for the window transparency.

As @kovidgoyal said, this is not the thread for such a discussion, and forgive my confusion as I thought the two were related.

ctrlcctrlv commented 4 years ago

Good progress is being made. The first problem has been overcome, and I understand how Kitty renders to the screen much better now.

2020-01-10-195432_2560x1401_scrot

kovidgoyal commented 4 years ago

That's gratifying :)

ctrlcctrlv commented 4 years ago

With #2271 out of the way I'm once again wondering if a "persist" flag which makes images survive screen clearing isn't the best way to implement this.

It would make the images window-local rather than kitty-global, and would play well with the session system, where users could set different backdrops for different tabs, or even configure more complex set ups where they cycle through images throughout the day, like a slideshow.

Are you totally against this @kovidgoyal ?

kovidgoyal commented 4 years ago

There will be no way to have a background image stretch across multiple windows (by this I mean windows in a tab). This will probably look pretty bad. You will basically have tiled images with borders between them, rather than a true background image that stretches under all windows.

And there is still the problem of making sure both that all sites that remove images know about persistent images and dealing with main/alternate screen transitions, you actually have two grman objects per Screen. These are not major problems of course, just additional state to keep track of.

I think we need to first settle on the desired semantics. Should bg images be per kitty window, per kitty tab, per kitty OS Window or per kitty process.

On Fri, Jan 10, 2020 at 10:26:19PM -0800, Fredrick Brennan wrote:

With #2271 out of the way I'm once again wondering if a "persist" flag which makes images survive screen clearing isn't the best way to implement this.

It would make the images window-local rather than kitty-global, and would play well with the session system, where users could set different backdrops for different tabs, or even configure more complex set ups where they cycle through images throughout the day, like a slideshow.

Are you totally against this @kovidgoyal ?

-- You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub: https://github.com/kovidgoyal/kitty/issues/163#issuecomment-573287423

--


Dr. Kovid Goyal https://www.kovidgoyal.net https://calibre-ebook.com


ctrlcctrlv commented 4 years ago

My vote is per kitty window, set in session file.

kovidgoyal commented 4 years ago

On Fri, Jan 10, 2020 at 11:01:27PM -0800, Fredrick Brennan wrote:

My vote is per kitty window, set in session file.

Well, in that case, setting it in the session file is not going to be enough. You will need to add it to the launch command so people can create windows with specific images, and the remote control protocol and probably as a kitty.conf setting that applies to all newly created windows in that process, unless overridden.

As for my vote, I am neutral, since I will personally never use this feature.

kovidgoyal commented 4 years ago

Let's leave it for a little while so other people can comment on their preferences, the tradeoff from a user perspective is:

With image-per-window you can set different images in every kitty window, but you cannot have a single image underlie neighboring kitty windows.

With image per tab you can set different images in different tabs but not different images in windows inside a tab. But you can have a single image stretch across all windows in a tab.

If you are interested in this feature and have apreference, vote now or forever hold your peace.

ctrlcctrlv commented 4 years ago

I'm happy with leaving it up to the users. I'm happy to implement either per OS window or per kitty window.

miaXcova commented 4 years ago

I vote per window.

ctrlcctrlv commented 4 years ago

Hello friends—I will begin implementation of per-kitty window background images, the voting is closed, that's what I decided on.

ctrlcctrlv commented 4 years ago

Looking at it more for the last half hour or so, I'm unfortunately not sure how to implement this in a sane way, even with #2271 solved.

The graphics protocol is not the way:

When resetting the terminal, all images that are visible on the screen must be cleared. [...] When scrolling the screen (such as when using index cursor movement commands, or scrolling through the history buffer), images must be scrolled along with text.

I'd be breaking many assumptions with a "persist" flag.

So it means maybe even the GraphicsManager itself is not the place to be putting this.

So then in shaders.c? In draw_cells_interleaved_premult and so on? Maybe using blit_vertex_array and drawing on top of that? Because I can't use any of the GraphicsManager stuff, it all assumes the same stuff as the graphics protocol.

Option parsing is simple, and we seem to have functions for inflating PNG into a bitmap in memory, but I don't know that I know enough OpenGL to get us all the way there. Sorry.

kovidgoyal commented 4 years ago

Yes, as I said storing a background in graphics manager is very hacky. For the architecture of the actual solution:

1) reuse the code in graphics.c to sentd the image data to the GPU

2) store the texture id for the image on the Window object, since it is per window, but probably have some mechanism to share across all windows in the process to avoid sending data of the same image more than once to the GPU

3) change the Window/OSWindow destructors to de-allocate the image. Maybe use reference counting to free the image texture only when no windows are using it anymore.

4) Create a simple shader that draws the background image. You can base it of the GRAPHICS_ALPHA_MASK_PROGRAM see draw_centered_alpha_mask but you will need to ad duniforms for scaling vs tiling, any applied tint, etc.

5) in the draw_cells function take the existence of the background image into consideration when deciding to call draw_cells_interleaved

6) In draw_cells_interleaved(_premult) simply add an extra draw call that uses the shader from (4) to draw the background image before doing anything else.

7) Create a config option for kitty.conf as well as a command for the remote control protocol in cmds.py to set the background image.

8) Deal with detaching of windows. When a windows is detached. When a window is detached to move it to another OS Window, if its background image already exists in that os window, release it and take a reference to that os windows image. If not, then add it to the os window and you might have to resend the image data as well, depends on the detail of how the graphics vao is shared, I dont remember of the top off my head.

On Tue, Jan 14, 2020 at 11:40:43PM -0800, Fredrick Brennan wrote:

Looking at it more for the last half hour or so, I'm unfortunately not sure how to implement this in a sane way, even with #2271 solved.

The graphics protocol is not the way:

When resetting the terminal, all images that are visible on the screen must be cleared. [...] When scrolling the screen (such as when using index cursor movement commands, or scrolling through the history buffer), images must be scrolled along with text.

I'd be breaking many assumptions with a "persist" flag.

So it means maybe even the GraphicsManager itself is not the place to be putting this.

So then in shaders.c? In draw_cells_interleaved_premult and so on? Maybe using blit_vertex_array and drawing on top of that? Because I can't use any of the GraphicsManager stuff, it all assumes the same stuff as the graphics protocol.

Option parsing is simple, and we seem to have functions for inflating PNG into a bitmap in memory, but I don't know that I know enough OpenGL to get us all the way there. Sorry.

-- You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub: https://github.com/kovidgoyal/kitty/issues/163#issuecomment-574535576

--


Dr. Kovid Goyal https://www.kovidgoyal.net https://calibre-ebook.com


ctrlcctrlv commented 4 years ago

Good plan. I'm going to need to study some toy GLFW programs as well, and understand better how OpenGL manages textures. As I understand it, if I can manage to load the (PNG) image in as a texture, (which is no problem, much of this code already exists) I should be able to get tiling and scaling for free just by correct use of shaders, vertices and GL parameters.

kovidgoyal commented 4 years ago

Yes, you just need to upload the data, and then the shader can instruct the GPU to do scaling/tiling automatically. Pretty much all modern OpenGL tutorials start with drawing a simple image using a shader.

On Wed, Jan 15, 2020 at 04:38:26AM -0800, Fredrick Brennan wrote:

Good plan. I'm going to need to study some toy GLFW programs as well, and understand better how OpenGL manages textures. As I understand it, if I can manage to load the (PNG) image in as a texture, (which is no problem, much of this code already exists) I should be able to get tiling and scaling for free just by correct use of shaders, vertices and GL parameters.

-- You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub: https://github.com/kovidgoyal/kitty/issues/163#issuecomment-574641679

--


Dr. Kovid Goyal https://www.kovidgoyal.net https://calibre-ebook.com


ctrlcctrlv commented 4 years ago

Thank you for your patience, and for laying out an architecture. FontForge just uses Cairo and GDK (component of GTK) to draw to screen. My first work with OpenGL at all was this year. This is a bit out of my element but I believe I can do it if I apply myself to your instructions.

kovidgoyal commented 4 years ago

You're welcome. OpenGL is an awful, awful API, but it's kind of a fun challenge to overcome. I myself had to learn OpenGL from scratch for building kitty. Keep in mind that kitty has to run on very old OpenGL 3.3 drivers (macOS).

On Wed, Jan 15, 2020 at 04:48:27AM -0800, Fredrick Brennan wrote:

Thank you for your patience, and for laying out an architecture. FontForge just uses Cairo and GDK (component of GTK) to draw to screen. My first work with OpenGL at all was this year. This is a bit out of my element but I believe I can do it if I apply myself to your instructions.

-- You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub: https://github.com/kovidgoyal/kitty/issues/163#issuecomment-574645149

--


Dr. Kovid Goyal https://www.kovidgoyal.net https://calibre-ebook.com


coxley commented 4 years ago

:smile: Woo!

ctrlcctrlv commented 4 years ago

I've continued working on it...but am fighting with OpenGL and am not sure what I'm doing wrong.

Under Nvidia drivers, this patch crashes with a segmentation fault. Under Mesa drivers, this patch doesn't draw anything, but doesn't crash.

Note: Even if the drawing is fixed, this patch is a super rough draft; there are no frees, or reference counting, et cetera.

Can you help please @kovidgoyal? The buggy function is draw_bg, and this patch is meant to be applied against 43326c9bd03044cb5cb6f00fd8b372becf2d206b

Config

background_image ~/tmp/Quiapo1.png
background_image_layout scaled
background_image_opacity 0.9

Patch ipsum

diff --git a/kitty/config_data.py b/kitty/config_data.py
index a3dbfeb2..c74ef888 100644
--- a/kitty/config_data.py
+++ b/kitty/config_data.py
@@ -822,6 +822,29 @@ of windows set :opt:`dynamic_background_opacity` to :code:`yes` (this is off by
 default as it has a performance cost)
 '''))

+def startup_session(x):
+    if x.lower() == 'none':
+        return
+    x = os.path.expanduser(x)
+    x = os.path.expandvars(x)
+    if not os.path.isabs(x):
+        x = os.path.join(config_dir, x)
+    return x
+
+
+o('background_image', 'none', option_type=startup_session, long_text=_('''
+Path to a background image. Must be PNG.'''))
+
+o('background_image_layout', 'tiling', option_type=choices('tiling', 'scaled'),
+long_text=_('''
+How to lay out the background image.'''))
+
+o('background_image_opacity', 0.5, option_type=positive_float, long_text=_('''
+Background image opacity, between 0.0 and 1.0 inclusive. This
+can only ever decrease a background's opacity, if the image is already
+semi-transparent, "1" is interpreted as the image's current transparency.'''))
+
+
 o('dynamic_background_opacity', False, long_text=_('''
 Allow changing of the :opt:`background_opacity` dynamically, using either keyboard
 shortcuts (:sc:`increase_background_opacity` and :sc:`decrease_background_opacity`)
@@ -942,16 +965,6 @@ The default is to check every 24 hrs, set to zero to disable.
 '''))

-def startup_session(x):
-    if x.lower() == 'none':
-        return
-    x = os.path.expanduser(x)
-    x = os.path.expandvars(x)
-    if not os.path.isabs(x):
-        x = os.path.join(config_dir, x)
-    return x
-
-
 o('startup_session', 'none', option_type=startup_session, long_text=_('''
 Path to a session file to use for all kitty instances. Can be overridden
 by using the :option:`kitty --session` command line option for individual
diff --git a/kitty/data-types.h b/kitty/data-types.h
index aabe751d..15cd687f 100644
--- a/kitty/data-types.h
+++ b/kitty/data-types.h
@@ -55,6 +55,7 @@ typedef enum MouseTrackingModes { NO_TRACKING, BUTTON_MODE, MOTION_MODE, ANY_MOD
 typedef enum MouseTrackingProtocols { NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL} MouseTrackingProtocol;
 typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
 typedef enum { NONE, MENUBAR, WINDOW, ALL } WindowTitleIn;
+typedef enum { TILING, SCALED } BackgroundImageLayout;

 #define MAX_CHILDREN 512
 #define BLANK_CHAR 0
diff --git a/kitty/gl.c b/kitty/gl.c
index cd623c8e..dc9c229a 100644
--- a/kitty/gl.c
+++ b/kitty/gl.c
@@ -38,6 +38,20 @@ check_for_gl_error(void UNUSED *ret, const char *name, GLADapiproc UNUSED funcpt
     }
 }

+void GLAPIENTRY
+MessageCallback( GLenum source,
+                 GLenum type,
+                 GLuint id,
+                 GLenum severity,
+                 GLsizei length,
+                 const GLchar* message,
+                 const void* userParam )
+{
+  fprintf( stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+           ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
+            type, severity, message );
+}
+
 void
 gl_init() {
     static bool glad_loaded = false;
@@ -63,6 +77,8 @@ gl_init() {
         if (gl_major < OPENGL_REQUIRED_VERSION_MAJOR || (gl_major == OPENGL_REQUIRED_VERSION_MAJOR && gl_minor < OPENGL_REQUIRED_VERSION_MINOR)) {
             fatal("OpenGL version is %d.%d, version >= 3.3 required for kitty", gl_major, gl_minor);
         }
+    glEnable(GL_DEBUG_OUTPUT);
+    glDebugMessageCallback( MessageCallback, 0 );
     }
 }

@@ -364,6 +380,7 @@ bind_vao_uniform_buffer(ssize_t vao_idx, size_t bufnum, GLuint block_index) {

 void
 unmap_vao_buffer(ssize_t vao_idx, size_t bufnum) {
+    printf("Unmapping VAO %li\n", vao_idx);
     ssize_t buf_idx = vaos[vao_idx].buffers[bufnum];
     unmap_buffer(buf_idx);
     unbind_buffer(buf_idx);
diff --git a/kitty/graphics.c b/kitty/graphics.c
index b1d49515..0e0c0928 100644
--- a/kitty/graphics.c
+++ b/kitty/graphics.c
@@ -29,10 +29,18 @@ static bool send_to_gpu = true;
 GraphicsManager*
 grman_alloc() {
     GraphicsManager *self = (GraphicsManager *)GraphicsManager_Type.tp_alloc(&GraphicsManager_Type, 0);
-    self->images_capacity = 64;
+    self->images_capacity = self->capacity = 64;
+    if (OPT(background_image) != NULL) { self->images_capacity++; self->capacity++; }
     self->images = calloc(self->images_capacity, sizeof(Image));
-    self->capacity = 64;
     self->render_data = calloc(self->capacity, sizeof(ImageRenderData));
+    printf("Bg image is %s\n", OPT(background_image));
+    if (OPT(background_image) != NULL) {
+        if (access(OPT(background_image), F_OK) != -1) {
+            log_error("File %s does exist", OPT(background_image));
+        } else {
+            log_error("File %s does not exist", OPT(background_image));
+        }
+    }
     if (self->images == NULL || self->render_data == NULL) {
         PyErr_NoMemory();
         Py_CLEAR(self); return NULL;
@@ -248,6 +256,39 @@ add_trim_predicate(Image *img) {
     return !img->data_loaded || (!img->client_id && !img->refcnt);
 }

+bool png_path_to_bitmap(uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz) {
+    const char* path = OPT(background_image);
+    if (access(path, R_OK) != 0) {
+        log_error("File %s, (requested background image,) does not exist (%d)", path, errno);
+        return false;
+    }
+    FILE* fp = fopen(path, "r");
+    if (fp == NULL) {
+        log_error("File %s, (requested background image,) could not be opened", path);
+        return false;
+    }
+    fseek(fp, 0L, SEEK_END);
+    size_t filesize = ftell(fp);
+    *data = calloc(filesize, sizeof(char));
+    fseek(fp, 0L, SEEK_SET); // rewind() deprecated on some platforms
+    fread(*data, sizeof(char), filesize, fp);
+    fclose(fp);
+    png_read_data d;
+    memset(&d, 0, sizeof(png_read_data));
+    inflate_png_inner(&d, *data, filesize);
+    if (!d.ok) {
+        log_error("File %s, (requested background image,) not readable by libpng", path);
+        return false;
+    }
+    free(*data);
+    *data = d.decompressed;
+    *sz = d.sz;
+    *height = d.height; *width = d.width;
+    //send_image_to_gpu(tex_id, data, 0, 0, false, true);
+    return true;
+}
+
+
 static inline Image*
 find_or_create_image(GraphicsManager *self, uint32_t id, bool *existing) {
     if (id) {
diff --git a/kitty/graphics.h b/kitty/graphics.h
index 16af6923..bc0b8abc 100644
--- a/kitty/graphics.h
+++ b/kitty/graphics.h
@@ -55,6 +55,12 @@ typedef struct {
     size_t used_storage;
 } Image;

+typedef struct {
+    uint32_t texture_id;
+    unsigned int height, width;
+    uint8_t* bitmap;
+} BackgroundImage;
+
 typedef struct {
     float vertices[16];
     uint32_t texture_id, group_count;
@@ -71,6 +77,8 @@ typedef struct {
     Image *images;
     size_t count, capacity;
     ImageRenderData *render_data;
+    Image bgimage;
+    ImageRenderData bgimage_rd;
     bool layers_dirty;
     // The number of images below MIN_ZINDEX / 2, then the number of refs between MIN_ZINDEX / 2 and -1 inclusive, then the number of refs above 0 inclusive.
     size_t num_of_below_refs, num_of_negative_refs, num_of_positive_refs;
@@ -93,3 +101,4 @@ void grman_scroll_images(GraphicsManager *self, const ScrollData*, CellPixelSize
 void grman_resize(GraphicsManager*, index_type, index_type, index_type, index_type);
 void grman_rescale(GraphicsManager *self, CellPixelSize fg);
 void gpu_data_for_centered_image(ImageRenderData *ans, unsigned int screen_width_px, unsigned int screen_height_px, unsigned int width, unsigned int height);
+bool png_path_to_bitmap(uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz);
diff --git a/kitty/screen.c b/kitty/screen.c
index dac33f41..802e46e2 100644
--- a/kitty/screen.c
+++ b/kitty/screen.c
@@ -111,6 +111,18 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
         self->historybuf = alloc_historybuf(MAX(scrollback, lines), columns, OPT(scrollback_pager_history_size));
         self->main_grman = grman_alloc();
         self->alt_grman = grman_alloc();
+
+        BackgroundImage* bgimage = calloc(1, sizeof(BackgroundImage));
+        size_t size;
+        bool has_bg = png_path_to_bitmap(&bgimage->bitmap, &bgimage->width, &bgimage->height, &size);
+        if (has_bg) {
+            printf("Sending image to GPU (%dx%d)\n", bgimage->width, bgimage->height);
+            bgimage->texture_id = 0;
+            send_image_to_gpu(&bgimage->texture_id, bgimage->bitmap, bgimage->width, bgimage->height, false, true);
+            printf("Texture ID we got was %d, len %lu\n", bgimage->texture_id, size);
+            self->bgimage = bgimage;
+        }
+
         self->grman = self->main_grman;
         self->pending_mode.wait_time = 2.0;
         self->disable_ligatures = OPT(disable_ligatures);
diff --git a/kitty/screen.h b/kitty/screen.h
index 307eb47b..c1f7340b 100644
--- a/kitty/screen.h
+++ b/kitty/screen.h
@@ -86,6 +86,7 @@ typedef struct {
     unsigned int history_line_added_count;
     bool *tabstops, *main_tabstops, *alt_tabstops;
     ScreenModes modes;
+    BackgroundImage *bgimage;
     ColorProfile *color_profile;
     monotonic_t start_visual_bell_at;

diff --git a/kitty/shaders.c b/kitty/shaders.c
index 818895b5..59c87ff7 100644
--- a/kitty/shaders.c
+++ b/kitty/shaders.c
@@ -7,6 +7,8 @@

 #include "fonts.h"
 #include "gl.h"
+#include "png-reader.h"
+#include <png.h>
 #include <stddef.h>

 enum { CELL_PROGRAM, CELL_BG_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FG_PROGRAM, BORDERS_PROGRAM, GRAPHICS_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_ALPHA_MASK_PROGRAM, BLIT_PROGRAM, NUM_PROGRAMS };
@@ -22,6 +24,7 @@ typedef struct {

 static const SpriteMap NEW_SPRITE_MAP = { .xnum = 1, .ynum = 1, .last_num_of_layers = 1, .last_ynum = -1 };
 static GLint max_texture_size = 0, max_array_texture_layers = 0;
+//static uint8_t *bg_image = NULL;

 SPRITE_MAP_HANDLE
 alloc_sprite_map(unsigned int cell_width, unsigned int cell_height) {
@@ -204,6 +207,7 @@ create_cell_vao() {

 ssize_t
 create_graphics_vao() {
+    printf("CGV CGV CGV\n");
     ssize_t vao_idx = create_vao();
     add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER);
     add_attribute_to_vao(GRAPHICS_PROGRAM, vao_idx, "src", 4, GL_FLOAT, 0, NULL, 0);
@@ -325,6 +329,20 @@ cell_prepare_to_render(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, GLfloa
     return changed;
 }

+static void
+draw_bg(int program, ssize_t vao_idx, ssize_t gvao_idx, BackgroundImage *bgimage) {
+    bind_program(program);
+    bind_vertex_array(gvao_idx);
+    glActiveTexture(GL_TEXTURE0 + GRAPHICS_UNIT);
+    printf("Drawing image with ID %d;\n", bgimage->texture_id);
+
+    glEnable(GL_SCISSOR_TEST);
+    glBindTexture(GL_TEXTURE_2D, bgimage->texture_id);
+    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+    glDisable(GL_SCISSOR_TEST);
+    bind_vertex_array(vao_idx);
+}
+
 static void
 draw_graphics(int program, ssize_t vao_idx, ssize_t gvao_idx, ImageRenderData *data, GLuint start, GLuint count) {
     bind_program(program);
@@ -400,6 +418,10 @@ draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen) {
     glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3);
     glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns);

+    if (screen->bgimage != NULL) {
+        draw_bg(GRAPHICS_PROGRAM, vao_idx, gvao_idx, screen->bgimage);
+    }
+
     if (screen->grman->num_of_below_refs) {
         draw_graphics(GRAPHICS_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs);
         bind_program(CELL_BG_PROGRAM);
@@ -443,6 +465,10 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen
     glEnable(GL_BLEND);
     BLEND_PREMULT;

+    if (screen->bgimage != NULL) {
+        draw_bg(GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->bgimage);
+    }
+
     if (screen->grman->num_of_below_refs) {
         draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs);
         bind_program(CELL_BG_PROGRAM);
@@ -555,10 +581,10 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat ystart, GL
     );
 #undef SCALE
     if (os_window->is_semi_transparent) {
-        if (screen->grman->count) draw_cells_interleaved_premult(vao_idx, gvao_idx, screen, os_window);
+        if (screen->grman->count || screen->bgimage != NULL) draw_cells_interleaved_premult(vao_idx, gvao_idx, screen, os_window);
         else draw_cells_simple(vao_idx, gvao_idx, screen);
     } else {
-        if (screen->grman->num_of_negative_refs || screen->grman->num_of_below_refs) draw_cells_interleaved(vao_idx, gvao_idx, screen);
+        if (screen->bgimage != NULL || screen->grman->num_of_negative_refs || screen->grman->num_of_below_refs) draw_cells_interleaved(vao_idx, gvao_idx, screen);
         else draw_cells_simple(vao_idx, gvao_idx, screen);
     }
 }
diff --git a/kitty/state.c b/kitty/state.c
index a73d9603..edb00143 100644
--- a/kitty/state.c
+++ b/kitty/state.c
@@ -101,6 +101,7 @@ add_tab(id_type os_window_id) {

 static inline void
 create_gpu_resources_for_window(Window *w) {
+    printf("Creating VAOs...\n");
     w->render_data.vao_idx = create_cell_vao();
     w->render_data.gvao_idx = create_graphics_vao();
 }
@@ -415,6 +416,16 @@ window_title_in(PyObject *title_in) {
     return ALL;
 }

+static BackgroundImageLayout bglayout(PyObject *layout_name) {
+    const char *name = PyUnicode_AsUTF8(layout_name);
+    switch(name[0]) {
+        case 't': return TILING;
+        case 's': return SCALED;
+        default: break;
+    }
+    return TILING;
+}
+
 static MouseShape
 pointer_shape(PyObject *shape_name) {
     const char *name = PyUnicode_AsUTF8(shape_name);
@@ -483,6 +494,9 @@ PYWRAP1(set_options) {
     S(cursor_blink_interval, parse_s_double_to_monotonic_t);
     S(cursor_stop_blinking_after, parse_s_double_to_monotonic_t);
     S(background_opacity, PyFloat_AsFloat);
+    S(background_image_opacity, PyFloat_AsFloat);
+    S(background_image_layout, bglayout);
+    S(background_image, (char*)PyUnicode_AsUTF8);
     S(dim_opacity, PyFloat_AsFloat);
     S(dynamic_background_opacity, PyObject_IsTrue);
     S(inactive_text_alpha, PyFloat_AsFloat);
diff --git a/kitty/state.h b/kitty/state.h
index fad1745a..c11336a1 100644
--- a/kitty/state.h
+++ b/kitty/state.h
@@ -37,6 +37,11 @@ typedef struct {
     int adjust_line_height_px, adjust_column_width_px;
     float adjust_line_height_frac, adjust_column_width_frac;
     float background_opacity, dim_opacity;
+
+    char* background_image; //path
+    BackgroundImageLayout background_image_layout;
+    float background_image_opacity;
+
     bool dynamic_background_opacity;
     float inactive_text_alpha;
     float window_padding_width;
kovidgoyal commented 4 years ago

try using --debug-gl

ctrlcctrlv commented 4 years ago

Yup, have done. I run kitty like this when developing:

CFLAGS="-g -O0 -Wno-error=unused-parameter" make&&DISPLAY=:1 LC_ALL=en_US.UTF-8 gdb -ex 'run --debug-gl' kitty/launcher/kitty

The :1 display is Xephyr running a software-only GL, for comparison against Nvidia.

kovidgoyal commented 4 years ago

I'm afraid it's going ot be a little while before I have time to look at this.

ctrlcctrlv commented 4 years ago

That's okay, I'll let you know if I end up figuring it out myself. I'm still thinking about it. Apparently on Windows Nvidia is now providing symbol tables for their drivers. But not on Linux.

I guess the problem is probably related to using the gvao to draw this. I might need to create another vao? The problem goes away if bind_vertex(gvao_idx); is removed in draw_bg function.

ctrlcctrlv commented 4 years ago

The problem might also be in the texture loading. On Mesa nothing is visibly different, so that makes me believe I messed something up there. Perhaps I should work on getting it on the screen in Mesa then it'll be fixed under Nvidia. :p