1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stddef.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <sane/sane.h>
7 #include <sane/saneopts.h>
8
9 #include "readers.h"
10 #include "viewer.h"
11 #include "sane.h"
12 #include "ida.h"
13
14 #include <X11/Intrinsic.h>
15 #include <Xm/Xm.h>
16 #include <Xm/PushB.h>
17 #include <Xm/CascadeB.h>
18 #include <Xm/RowColumn.h>
19
20 extern int sane_res;
21
22 /* ---------------------------------------------------------------------- */
23
24 static void
build_menu(Widget widget,XtPointer clientdata,XtPointer call_data)25 build_menu(Widget widget, XtPointer clientdata, XtPointer call_data)
26 {
27 WidgetList children,wlist;
28 Cardinal nchildren;
29 const SANE_Device **list;
30 Widget push;
31 XmString str;
32 char action[256];
33 int rc,i;
34
35 /* del old */
36 XtVaGetValues(widget,
37 XtNchildren,&children,
38 XtNnumChildren,&nchildren,
39 NULL);
40 wlist = malloc(sizeof(Widget*)*nchildren);
41 memcpy(wlist,children,sizeof(Widget*)*nchildren);
42 for (i = 0; i < nchildren; i++)
43 XtDestroyWidget(wlist[i]);
44 free(wlist);
45
46 /* create new */
47 if (SANE_STATUS_GOOD != (rc = sane_init(NULL,NULL))) {
48 fprintf(stderr,"sane_init: %s\n",sane_strstatus(rc));
49 goto done;
50 }
51 sane_get_devices(&list,0);
52 if (NULL == list[0])
53 goto done;
54
55 for (i = 0; list[i] != NULL; i++) {
56 if (debug)
57 fprintf(stderr,"sane dev: %s | %s | %s | %s\n",
58 list[i]->name, list[i]->vendor,
59 list[i]->model, list[i]->type);
60 str = XmStringGenerate((char*)list[i]->model,
61 NULL, XmMULTIBYTE_TEXT, NULL);
62 push = XtVaCreateManagedWidget(list[i]->name,
63 xmPushButtonWidgetClass,widget,
64 XmNlabelString,str,
65 NULL);
66 XmStringFree(str);
67 sprintf(action,"Scan(%s)",list[i]->name);
68 XtAddCallback(push,XmNactivateCallback,action_cb,strdup(action));
69 }
70
71 done:
72 sane_exit();
73 }
74
75 void
sane_menu(Widget menu)76 sane_menu(Widget menu)
77 {
78 Widget submenu;
79 int rc;
80
81 if (SANE_STATUS_GOOD != (rc = sane_init(NULL,NULL))) {
82 fprintf(stderr,"sane_init: %s\n",sane_strstatus(rc));
83 goto done;
84 }
85 submenu = XmCreatePulldownMenu(menu,"scanM",NULL,0);
86 XtVaCreateManagedWidget("scan",xmCascadeButtonWidgetClass,menu,
87 XmNsubMenuId,submenu,NULL);
88 XtAddCallback(submenu, XmNmapCallback, build_menu, NULL);
89
90 done:
91 sane_exit();
92 }
93
94 /* ---------------------------------------------------------------------- */
95 /* load */
96
97 #define BUF_LINES 16 /* read bigger chunks to reduce overhead */
98
99 struct sane_state {
100 SANE_Handle sane;
101 SANE_Parameters parm;
102 SANE_Byte *buf;
103 int started;
104 };
105
dump_desc(SANE_Handle h,int nr,const SANE_Option_Descriptor * opt)106 static void dump_desc(SANE_Handle h, int nr, const SANE_Option_Descriptor *opt)
107 {
108 SANE_Bool b;
109 SANE_Int i,flags;
110
111 fprintf(stderr,"sane opt: name=%s title=%s type=%d unit=%d cap=%d\n",
112 opt->name, opt->title, opt->type, opt->unit, opt->cap);
113 switch (opt->type) {
114 case SANE_TYPE_BOOL:
115 sane_control_option(h, nr, SANE_ACTION_GET_VALUE,
116 &b, &flags);
117 fprintf(stderr," value=%s [bool]\n",b ? "true" : "false");
118 break;
119 case SANE_TYPE_INT:
120 sane_control_option(h, nr, SANE_ACTION_GET_VALUE,
121 &i, &flags);
122 fprintf(stderr," value=%d [int]\n",i);
123 break;
124 case SANE_TYPE_FIXED:
125 case SANE_TYPE_STRING:
126 case SANE_TYPE_BUTTON:
127 case SANE_TYPE_GROUP:
128 break;
129 }
130 switch (opt->constraint_type) {
131 case SANE_CONSTRAINT_NONE:
132 break;
133 case SANE_CONSTRAINT_RANGE:
134 fprintf(stderr," range=%d-%d\n",
135 opt->constraint.range->min,
136 opt->constraint.range->max);
137 break;
138 case SANE_CONSTRAINT_WORD_LIST:
139 fprintf(stderr," constraint word_list:");
140 for (i = 1; i <= opt->constraint.word_list[0]; i++)
141 fprintf(stderr," %d",opt->constraint.word_list[i]);
142 fprintf(stderr,"\n");
143 break;
144 case SANE_CONSTRAINT_STRING_LIST:
145 fprintf(stderr," constraint string_list:");
146 for (i = 0; opt->constraint.string_list[i] != NULL; i++)
147 fprintf(stderr," %s",opt->constraint.string_list[i]);
148 fprintf(stderr,"\n");
149 break;
150 };
151 }
152
153 static void*
sane_idainit(FILE * fp,char * filename,unsigned int page,struct ida_image_info * info,int thumbnail)154 sane_idainit(FILE *fp, char *filename, unsigned int page, struct ida_image_info *info,
155 int thumbnail)
156 {
157 const SANE_Option_Descriptor *opt;
158 SANE_Int flags, count;
159 struct sane_state *h;
160 int rc,i,value,dpi = 0;
161
162 h = malloc(sizeof(*h));
163 memset(h,0,sizeof(*h));
164
165 if (SANE_STATUS_GOOD != (rc = sane_init(NULL,NULL))) {
166 fprintf(stderr,"sane_init: %s\n",sane_strstatus(rc));
167 goto oops;
168 }
169 if (SANE_STATUS_GOOD != (rc = sane_open(filename,&h->sane))) {
170 fprintf(stderr,"sane_open: %s\n",sane_strstatus(rc));
171 goto oops;
172 }
173
174 /* set options */
175 opt = sane_get_option_descriptor(h->sane,0);
176 rc = sane_control_option(h->sane, 0, SANE_ACTION_GET_VALUE,
177 &count, &flags);
178 for (i = 1; i < count; i++) {
179 opt = sane_get_option_descriptor(h->sane,i);
180 if (opt->name && 0 == strcmp(opt->name,SANE_NAME_SCAN_TL_X)) {
181 value = opt->constraint.range->min;
182 sane_control_option(h->sane, i, SANE_ACTION_SET_VALUE,
183 &value, &flags);
184 } else if (opt->name && 0 == strcmp(opt->name,SANE_NAME_SCAN_TL_Y)) {
185 value = opt->constraint.range->min;
186 sane_control_option(h->sane, i, SANE_ACTION_SET_VALUE,
187 &value, &flags);
188 } else if (opt->name && 0 == strcmp(opt->name,SANE_NAME_SCAN_BR_X)) {
189 value = opt->constraint.range->max;
190 sane_control_option(h->sane, i, SANE_ACTION_SET_VALUE,
191 &value, &flags);
192 } else if (opt->name && 0 == strcmp(opt->name,SANE_NAME_SCAN_BR_Y)) {
193 value = opt->constraint.range->max;
194 sane_control_option(h->sane, i, SANE_ACTION_SET_VALUE,
195 &value, &flags);
196 } else if (opt->name && 0 == strcmp(opt->name,SANE_NAME_PREVIEW)) {
197 value = SANE_FALSE;
198 sane_control_option(h->sane, i, SANE_ACTION_SET_VALUE,
199 &value, &flags);
200 } else if (opt->cap & SANE_CAP_AUTOMATIC)
201 sane_control_option(h->sane, i, SANE_ACTION_SET_AUTO,
202 NULL, &flags);
203 if (opt->name && 0 == strcmp(opt->name,SANE_NAME_SCAN_RESOLUTION)) {
204 if (sane_res) {
205 dpi = sane_res;
206 sane_control_option(h->sane, i, SANE_ACTION_SET_VALUE,
207 &dpi, &flags);
208 }
209 sane_control_option(h->sane, i, SANE_ACTION_GET_VALUE,
210 &dpi, &flags);
211 }
212 if (debug)
213 dump_desc(h->sane,i,opt);
214 }
215
216 if (SANE_STATUS_GOOD != (rc = sane_start(h->sane))) {
217 fprintf(stderr,"sane_start: %s\n",sane_strstatus(rc));
218 goto oops;
219 }
220 h->started = 1;
221
222 if (SANE_STATUS_GOOD != (rc = sane_get_parameters(h->sane,&h->parm))) {
223 fprintf(stderr,"sane_get_parameters: %s\n",sane_strstatus(rc));
224 goto oops;
225 }
226
227 if (h->parm.format != SANE_FRAME_GRAY &&
228 h->parm.format != SANE_FRAME_RGB) {
229 fprintf(stderr,"sane: unsupported frame format (%d)\n",h->parm.format);
230 goto oops;
231 }
232 if (h->parm.depth != 8) {
233 fprintf(stderr,"sane: unsupported color depth (%d)\n",h->parm.depth);
234 goto oops;
235 }
236 if (-1 == h->parm.lines) {
237 fprintf(stderr,"sane: can't handle unknown image size\n");
238 goto oops;
239 }
240
241 info->width = h->parm.pixels_per_line;
242 info->height = h->parm.lines;
243 if (dpi)
244 info->dpi = dpi;
245 h->buf = malloc(h->parm.bytes_per_line * BUF_LINES);
246 if (debug)
247 fprintf(stderr,"sane: scanning %ux%u %s\n",info->width,info->height,
248 (h->parm.format == SANE_FRAME_GRAY) ? "gray" : "color");
249
250 return h;
251
252 oops:
253 if (h->buf)
254 free(h->buf);
255 if (h->started)
256 sane_cancel(h->sane);
257 if (h->sane)
258 sane_close(h->sane);
259 sane_exit();
260 free(h);
261 return NULL;
262 }
263
264 static void
sane_idaread(unsigned char * dst,unsigned int line,void * data)265 sane_idaread(unsigned char *dst, unsigned int line, void *data)
266 {
267 struct sane_state *h = data;
268 unsigned int lines, total, offset, len;
269 int rc, i;
270 SANE_Byte *row;
271
272 if (0 == (line % BUF_LINES)) {
273 lines = BUF_LINES;
274 if (lines > h->parm.lines - line)
275 lines = h->parm.lines - line;
276 total = h->parm.bytes_per_line * lines;
277 offset = 0;
278 while (offset < total) {
279 rc = sane_read(h->sane, h->buf + offset, total - offset, &len);
280 if (rc != SANE_STATUS_GOOD)
281 return;
282 offset += len;
283 }
284 }
285 row = h->buf + (line % BUF_LINES) * h->parm.bytes_per_line;
286 switch (h->parm.format) {
287 case SANE_FRAME_GRAY:
288 for (i = 0; i < h->parm.pixels_per_line; i++) {
289 dst[3*i+0] = row[i];
290 dst[3*i+1] = row[i];
291 dst[3*i+2] = row[i];
292 }
293 break;
294 case SANE_FRAME_RGB:
295 memcpy(dst,row,h->parm.pixels_per_line*3);
296 break;
297 default:
298 fprintf(stderr,"sane: read: internal error\n");
299 exit(1);
300 }
301 }
302
303 static void
sane_idadone(void * data)304 sane_idadone(void *data)
305 {
306 struct sane_state *h = data;
307
308 sane_cancel(h->sane);
309 sane_close(h->sane);
310 sane_exit();
311 free(h->buf);
312 free(h);
313 }
314
315 struct ida_loader sane_loader = {
316 name: "sane interface",
317 init: sane_idainit,
318 read: sane_idaread,
319 done: sane_idadone,
320 };
321
init_rd(void)322 static void __init init_rd(void)
323 {
324 load_register(&sane_loader);
325 }
326