summaryrefslogtreecommitdiff
path: root/src/fetch.c
blob: 270089e34b238ef088ce072ab2063ad6178797b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
 * vfetch - neofetch but the ascii part is a video
 *
 * Copyright (C) 2026  Doan Luu Duc Tai
 *
 */

#include "fetch.h"
#include <glib.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "utils.h"

static VfetchError try_fastfetch(AppState *state, const VfetchArgs *args) {
  VfetchError err =
      run_command_fork_output("fastfetch --logo none --pipe false",
                              &state->live_fetch_output,
                              args->debug);
  if (err == VFETCH_SUCCESS) {
    state->fetch_command = g_strdup("fastfetch --logo none --pipe false");
    if (!state->fetch_command) {
      return VFETCH_ERROR_MEMORY_ALLOCATION;
    }
  }

  return err;
}

static VfetchError try_neofetch(AppState *state, const VfetchArgs *args) {
  log_verbose(state,
              args->verbose,
              args->debug,
              "fastfetch failed, trying neofetch...");

  VfetchError err = run_command_fork_output(
      "neofetch --off", &state->live_fetch_output, args->debug);
  if (err == VFETCH_SUCCESS) {
    state->fetch_command = g_strdup("neofetch --off");
    if (!state->fetch_command) {
      err = VFETCH_ERROR_MEMORY_ALLOCATION;
      return err;
    }
  }

  return err;
}

static VfetchError set_fetch_unavailable(void) {
  (void)fputs("\nERROR: Neither 'fastfetch' nor 'neofetch' could be found in "
              "your system's PATH.\n\n",
              stderr);
  (void)fputs("  Action Required:\n", stderr);
  (void)fputs("    - Please install either 'fastfetch' or 'neofetch'.\n",
              stderr);
  (void)fputs("    - Alternatively, to disable the system information display, "
              "use the '--fetch-tool none' flag.\n\n",
              stderr);

  return VFETCH_ERROR_COMMAND_FAILED;
}

static VfetchError fail_fetch_unavailable_override(const char *tool_name) {
  (void)fprintf(stderr,
                "\nERROR: The specified fetch tool ('%s') is not available in "
                "your system's PATH.\n\n",
                tool_name);
  (void)fputs("  Action Required:\n", stderr);
  (void)fputs(
      "    - Please install the tool or correct the --fetch-tool argument.\n",
      stderr);
  (void)fputs("    - To disable the system information display, use "
              "'--fetch-tool none'.\n\n",
              stderr);

  return VFETCH_ERROR_COMMAND_FAILED;
}

static VfetchError handle_fetch_override(AppState *state,
                                         const VfetchArgs *args) {
  log_verbose(state,
              args->verbose,
              args->debug,
              "User specified fetch tool: '%s'",
              args->fetch_tool_arg);

  if (strcmp(args->fetch_tool_arg, "fastfetch") == 0) {
    if (try_fastfetch(state, args) != VFETCH_SUCCESS) {
      return fail_fetch_unavailable_override("fastfetch");
    }
  } else if (strcmp(args->fetch_tool_arg, "neofetch") == 0) {
    if (try_neofetch(state, args) != VFETCH_SUCCESS) {
      return fail_fetch_unavailable_override("neofetch");
    }
  }

  return VFETCH_SUCCESS;
}

static VfetchError handle_fetch_fallback(AppState *state,
                                         const VfetchArgs *args) {
  log_verbose(state,
              args->verbose,
              args->debug,
              "No fetch tool specified, using fallback logic.");

  if (try_fastfetch(state, args) == VFETCH_SUCCESS) {
    return VFETCH_SUCCESS;
  }
  if (try_neofetch(state, args) == VFETCH_SUCCESS) {
    return VFETCH_SUCCESS;
  }

  return set_fetch_unavailable();
}

VfetchError initialize_fetch_utility(AppState *state, const VfetchArgs *args) {
  log_verbose(
      state, args->verbose, args->debug, "Initializing fetch utility...");

  if (args->fetch_tool_arg) {
    return handle_fetch_override(state, args);
  }

  return handle_fetch_fallback(state, args);
}

VfetchError start_fetch_thread(AppState *state, VfetchArgs *args) {
  if (args->fetch_tool_arg && strcmp(args->fetch_tool_arg, "none") == 0) {
    state->fetch_command = g_strdup("echo");
    state->live_fetch_output = g_strdup("");
    if (!state->fetch_command || !state->live_fetch_output) {
      return VFETCH_ERROR_MEMORY_ALLOCATION;
    }
    return VFETCH_SUCCESS;
  }

  if (!state->fetch_command) {
    VfetchError err = initialize_fetch_utility(state, args);
    if (err) {
      return err;
    }
  }

  atomic_store_explicit(&state->last_fetch_lines,
                        count_lines(state->live_fetch_output),
                        memory_order_relaxed);

  return VFETCH_SUCCESS;
}

#define K_DEFAULT_TERMINAL_WIDTH 80
void render_fetch_panel(char *screen_buffer,
                        size_t screen_buffer_size,
                        size_t *current_len,
                        AppState *state,
                        const VfetchArgs *args) {
  struct winsize window_size = {0};
  char *fetch_output_copy = NULL;

  size_t terminal_width = K_DEFAULT_TERMINAL_WIDTH;
  if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &window_size) == 0 &&
      window_size.ws_col > 0) {
    terminal_width = window_size.ws_col;
  }

  (void)pthread_mutex_lock(&state->fetch_mutex);
  if (state->live_fetch_output) {
    fetch_output_copy = g_strdup(state->live_fetch_output);
  }
  size_t current_fetch_lines =
      atomic_load_explicit(&state->last_fetch_lines, memory_order_relaxed);
  size_t prev_fetch_lines = state->previous_fetch_lines;
  state->previous_fetch_lines = current_fetch_lines;
  (void)pthread_mutex_unlock(&state->fetch_mutex);

  uint32_t fetch_x = args->pad_x + args->width + 4;
  size_t clear_width = terminal_width > fetch_x ? terminal_width - fetch_x : 0;
  size_t lines_to_clear = (prev_fetch_lines > current_fetch_lines)
                              ? prev_fetch_lines
                              : current_fetch_lines;
  if (lines_to_clear > 0 && clear_width > 0) {
    Rect rect_to_clear = {.height = lines_to_clear,
                          .width = clear_width,
                          .start_x = fetch_x,
                          .start_y = args->pad_y};
    clear_rect_in_buffer(
        screen_buffer, screen_buffer_size, current_len, &rect_to_clear);
  }

  const char *fetch_output_to_draw =
      fetch_output_copy ? fetch_output_copy : "...";
  append_block_to_buffer(screen_buffer,
                         screen_buffer_size,
                         current_len,
                         fetch_x,
                         args->pad_y,
                         fetch_output_to_draw,
                         strlen(fetch_output_to_draw));

  g_free(fetch_output_copy);
}
#undef K_DEFAULT_TERMINAL_WIDTH